diff --git a/dip3.css b/dip3.css
index b80137e..cb93d52 100755
--- a/dip3.css
+++ b/dip3.css
@@ -76,7 +76,7 @@ pre, kbd, samp, code, var, .b, pre span {
.u {
font: medium/1.75 'Arial Unicode MS', FreeSerif, OpenSymbol, 'DejaVu Sans', sans-serif;
}
-pre .u, pre .u span, .a {
+pre .u, td .u, pre .u span, .a {
font: medium/1.75 'Arial Unicode MS', 'DejaVu Sans', FreeSerif, OpenSymbol, sans-serif;
}
.baa {
diff --git a/strings.html b/strings.html
index 9ea13a6..299f9a1 100755
--- a/strings.html
+++ b/strings.html
@@ -93,7 +93,7 @@ My alphabet starts where your alphabet ends! ❞
&m
'深入 Python 3'
') or double quotes (").
-len() function returns the length of the string, i.e. the number of characters. This is the same function you use to find the length of a list. A string is like a list of characters.
+len() function returns the length of the string, i.e. the number of characters. This is the same function you use to find the length of a list, tuple, set, or dictionary. A string is like a tuple of characters.
+ operator.
TestCase class of the unittest module. This class provides many useful methods which you can use in your test case to test specific conditions.
-to_roman() function. (Well, the function hasn’t be written yet, but once it is, this is the line that will call it.) Notice that you have now defined the API for the to_roman() function: it must take an integer (the number to convert) and return a string (the Roman numeral representation). If the API is different than that, this test is considered failed. Also notice that you are not trapping any exceptions when you call to_roman(). This is intentional. to_roman() shouldn’t raise an exception when you call it with valid input, and these input values are all valid. If to_roman() raises an exception, this test is considered failed.
to_roman() function was defined correctly, called correctly, completed successfully, and returned a value, the last step is to check whether it returned the right value. This is a common question, and the TestCase class provides a method, assertEqual, to check whether two values are equal. If the result returned from to_roman() (result) does not match the known value you were expecting (numeral), assertEqual will raise an exception and the test will fail. If the two values are equal, assertEqual will do nothing. If every value returned from to_roman() matches the known value you expect, assertEqual never raises an exception, so test_to_roman_known_values eventually exits normally, which means to_roman() has passed this test.
Once you have a test case, you can start coding the to_roman() function. First, you should stub it out as an empty function and make sure the tests fail. If the tests succeed before you’ve written any code, you’re doing it wrong — your tests aren’t testing your code at all! Write a test that fails, then code until it passes.
+
Once you have a test case, you can start coding the to_roman() function. First, you should stub it out as an empty function and make sure the tests fail. If the tests succeed before you’ve written any code, your tests aren’t testing your code at all! Unit testing is a dance: tests lead, code follows. Write a test that fails, then code until it passes.
# roman1.py
def to_roman(n):
@@ -166,7 +166,7 @@ Traceback (most recent call last):
For each test case, the unittest module will print out the docstring of the method and whether that test passed or failed. As expected, this test case fails.
For each failed test case, unittest displays the trace information showing exactly what happened. In this case, the call to assertEqual() raised an AssertionError because it was expecting to_roman(1) to return 'I', but it didn’t. (Since there was no explicit return statement, the function returned None, the Python null value.)
After the detail of each test, unittest displays a summary of how many tests were performed and how long it took.
- Overall, the unit test failed because at least one test case did not pass. When a test case doesn’t pass, unittest distinguishes between failures and errors. A failure is a call to an assertXYZ method, like assertEqual or assertRaises, that fails because the asserted condition is not true or the expected exception was not raised. An error is any other sort of exception raised in the code you’re testing or the unit test case itself.
+ Overall, the test run failed because at least one test case did not pass. When a test case doesn’t pass, unittest distinguishes between failures and errors. A failure is a call to an assertXYZ method, like assertEqual or assertRaises, that fails because the asserted condition is not true or the expected exception was not raised. An error is any other sort of exception raised in the code you’re testing or the unit test case itself.
Now, finally, you can write the to_roman() function.
[download roman1.py]
@@ -461,7 +461,7 @@ OK
>>> import roman3
>>> roman3.to_roman(0.5) ①
''
->>> roman3.to_roman(1.5) ②
+>>> roman3.to_roman(1.0) ②
'I'
There’s a pleasing symmetry here. The to_roman() and from_roman() functions are inverses of each other. The first converts integers to specially-formatted strings, the second converts specially-formated strings to integers. In theory, we should be able to “round-trip” a number by passing to the to_roman() function to get a string, then passing that string to the from_roman() function to get an integer, and end up with the same number. In mathematical terms,
+
There’s a pleasing symmetry here. The to_roman() and from_roman() functions are inverses of each other. The first converts integers to specially-formatted strings, the second converts specially-formated strings to integers. In theory, we should be able to “round-trip” a number by passing to the to_roman() function to get a string, then passing that string to the from_roman() function to get an integer, and end up with the same number.
-
x = f(g(x)) for all values of x
+n = from_roman(to_roman(n)) for all values of n
In this case, “all values” means any number between 1..3999, since that is the valid range of inputs to the to_roman() function. We can express this symmetry in a test case that runs through all the values 1..3999, calls to_roman(), calls from_roman(), and checks that the output is the same as the original input.
@@ -618,6 +618,8 @@ FAILED (errors=2)
def from_roman(s):
'''convert Roman numeral to integer'''
+
(Hey, did you notice that? I defined a function with nothing but a docstring. That’s legal Python. In fact, some programmers swear by it. “Don’t stub; document!”) +
Now the test cases will actually fail.
@@ -652,7 +654,7 @@ FAILED (failures=2)"""convert Roman numeral to integer""" result = 0 index = 0 - for numeral, integer in romanNumeralMap: + for numeral, integer in roman_numeral_map: while s[index:index+len(numeral)] == numeral: ① result += integer index += len(numeral) @@ -667,7 +669,7 @@ FAILED (failures=2) """convert Roman numeral to integer""" result = 0 index = 0 - for numeral, integer in romanNumeralMap: + for numeral, integer in roman_numeral_map: while s[index:index+len(numeral)] == numeral: result += integer index += len(numeral) @@ -706,11 +708,11 @@ OK
As you saw in Case Study: Roman Numerals, there are several simple rules for constructing a Roman numeral, using the letters M, D, C, L, X, V, and I. Let's review the rules:
I is 1, II is 2, and III is 3. VI is 6 (literally, “5 and 1”), VII is 7, and VIII is 8.
+I is 1, II is 2, and III is 3. VI is 6 (literally, “5 and 1”), VII is 7, and VIII is 8.
I, X, C, and M) can be repeated up to three times. At 4, you need to subtract from the next highest fives character. You can't represent 4 as IIII; instead, it is represented as IV (“1 less than 5”). 40 is written as XL (“10 less than 50”), 41 as XLI, 42 as XLII, 43 as XLIII, and then 44 as XLIV (“10 less than 50, then 1 less than 5”).
-9, you need to subtract from the next highest tens character: 8 is VIII, but 9 is IX (“1 less than 10”), not VIIII (since the I character can not be repeated four times). 90 is XC, 900 is CM.
+9, you need to subtract from the next highest tens character: 8 is VIII, but 9 is IX (“1 less than 10”), not VIIII (since the I character can not be repeated four times). 90 is XC, 900 is CM.
10 is always represented as X, never as VV. 100 is always C, never LL.
-DC is 600; CD is a completely different number (400, “100 less than 500”). CI is 101; IC is not even a valid Roman numeral (because you can't subtract 1 directly from 100; you would need to write it as XCIX, “10 less than 100, then 1 less than 10”).
+DC is 600; CD is a completely different number (400, “100 less than 500”). CI is 101; IC is not even a valid Roman numeral (because you can't subtract 1 directly from 100; you would need to write it as XCIX, “10 less than 100, then 1 less than 10”).
Thus, one useful test would be to ensure that the from_roman() function should fail when you pass it a string with too many repeated numerals. How many is “too many” depends on the numeral.
@@ -728,7 +730,7 @@ OK
for s in ('CMCM', 'CDCD', 'XCXC', 'XLXL', 'IXIX', 'IVIV'):
self.assertRaises(roman6.InvalidRomanNumeralError, roman6.from_roman, s)
-
A third test could check that numerals appear in the correct order, from highest to lowest value. For example, CL is 150, but LC is never valid, because the numeral for 50 can never come before the numeral for 100.
+
A third test could check that numerals appear in the correct order, from highest to lowest value. For example, CL is 150, but LC is never valid, because the numeral for 50 can never come before the numeral for 100. This test includes a randomly chosen set of invalid antecedents: I before M, V before X, and so on.
def test_malformed_antecedents(self):
'''from_roman should fail with malformed antecedents'''
diff --git a/util/lesscss.py b/util/lesscss.py
index e1c8bc2..7f5896a 100755
--- a/util/lesscss.py
+++ b/util/lesscss.py
@@ -7,7 +7,7 @@ import sys
# These selectors are kept regardless of whether this script thinks they are used.
# Most of these match nodes that are dynamically inserted or manipulated by script
# after the page has loaded, which is why a static analysis thinks they're unused.
-SELECTOR_EXCEPTIONS = ('.w', '.b', '.str', '.kwd', '.com', '.typ', '.lit', '.pun', '.tag', '.atn', '.atv', '.dec', 'pre .u', 'pre .u span', 'li ol', 'a.hl:link', 'a.hl:visited', 'a.hl:hover', 'h2[id]:hover a.hl', 'h3[id]:hover a.hl')
+SELECTOR_EXCEPTIONS = ('.w', '.b', '.str', '.kwd', '.com', '.typ', '.lit', '.pun', '.tag', '.atn', '.atv', '.dec', 'pre .u', 'pre .u span', 'td .u', 'li ol', 'a.hl:link', 'a.hl:visited', 'a.hl:hover', 'h2[id]:hover a.hl', 'h3[id]:hover a.hl')
filename = sys.argv[1]
pqd = pq(filename=filename)
diff --git a/your-first-python-program.html b/your-first-python-program.html
index 049b66b..2997927 100755
--- a/your-first-python-program.html
+++ b/your-first-python-program.html
@@ -275,7 +275,7 @@ SyntaxError: non-keyword arg after keyword arg
if size < 0:
raise ValueError('number must be non-negative')
-The syntax for raising an exception is simple enough. Use the raise statement, followed by the exception name, and an optional human-readable string for debugging purposes. The syntax is reminiscent of calling a function. (In reality, exceptions are implemented as classes, and this raise statement is actually creating an instance of the ValueError class and passing the string 'number must be non-negative' to its initialization method. But we’re getting ahead of ourselves ! )
+
The syntax for raising an exception is simple enough. Use the raise statement, followed by the exception name, and an optional human-readable string for debugging purposes. The syntax is reminiscent of calling a function. (In reality, exceptions are implemented as classes, and this raise statement is actually creating an instance of the ValueError class and passing the string 'number must be non-negative' to its initialization method. But we’re getting ahead of ourselves!)
☞You don’t need to handle an exception in the function that raises it. If one function doesn’t handle it, the exception is passed to the calling function, then that function’s calling function, and so on “up the stack.” If the exception is never handled, your program will crash, Python will print a “traceback” to standard error, and that’s the end of that. Again, maybe that’s what you want; it depends on what your program does. @@ -306,7 +306,7 @@ except ImportError:
By the end of this
try..exceptblock, you have imported some module and named it etree. Since both modules implement a common API, the rest of your code doesn’t need to keep checking which module got imported. And since the module that did get imported is always called etree, the rest of your code doesn’t need to be littered withifstatements to call differently-named modules. -Unbound Variables
+Unbound Variables
Take another look at this line of code from the
approximate_size()function: @@ -324,6 +324,30 @@ NameError: name 'x' is not definedYou will thank Python for this one day. +
Everything is Case-Sensitive
+ +All names in Python are case-sensitive: variable names, function names, class names, module names, exception names. If you can get it, set it, call it, construct it, import it, or raise it, it’s case-sensitive. + +
+>>> an_integer = 1 +>>> an_integer +1 +>>> AN_INTEGER +Traceback (most recent call last): + File "<stdin>", line 1, in <module> +NameError: name 'AN_INTEGER' is not defined +>>> An_Integer +Traceback (most recent call last): + File "<stdin>", line 1, in <module> +NameError: name 'An_Integer' is not defined +>>> an_inteGer +Traceback (most recent call last): + File "<stdin>", line 1, in <module> +NameError: name 'an_inteGer' is not defined ++ +And so on. +
⁂
Running Scripts