From 654b102d74834dff054b3948c73ce7823a0f791e Mon Sep 17 00:00:00 2001 From: Mark Pilgrim Date: Fri, 5 Jun 2009 23:39:50 -0400 Subject: [PATCH] you wouldn't believe me if I told you --- about.html | 2 +- advanced-classes.html | 2 +- advanced-iterators.html | 9 +- case-study-porting-chardet-to-python-3.html | 14 +- dip3.css | 26 +-- examples/alphametics.py | 4 +- examples/alphameticstest.py | 30 ++-- examples/feed-broken.xml | 48 ++--- examples/feed-ns0.xml | 48 ++--- examples/feed.xml | 48 ++--- examples/fibonacci.py | 2 +- examples/fibonacci2.py | 4 +- examples/humansize.py | 12 +- examples/plural1.py | 4 +- examples/plural2.py | 4 +- examples/plural3.py | 4 +- examples/plural4.py | 4 +- examples/plural5.py | 4 +- examples/plural6.py | 4 +- examples/pluraltest1.py | 12 +- examples/pluraltest2.py | 12 +- examples/pluraltest3.py | 12 +- examples/pluraltest4.py | 12 +- examples/pluraltest5.py | 15 +- examples/pluraltest6.py | 37 ++-- examples/roman1.py | 10 +- examples/roman10.py | 22 +-- examples/roman2.py | 12 +- examples/roman3.py | 12 +- examples/roman4.py | 14 +- examples/roman5.py | 16 +- examples/roman6.py | 22 +-- examples/roman7.py | 24 +-- examples/roman8.py | 26 +-- examples/roman9.py | 26 +-- examples/romantest1.py | 10 +- examples/romantest10.py | 34 ++-- examples/romantest2.py | 12 +- examples/romantest3.py | 16 +- examples/romantest4.py | 18 +- examples/romantest5.py | 22 +-- examples/romantest6.py | 28 +-- examples/romantest7.py | 30 ++-- examples/romantest8.py | 34 ++-- examples/romantest9.py | 34 ++-- files.html | 2 +- generators.html | 2 +- http-web-services.html | 25 +-- index.html | 12 +- iterators-and-generators.html | 21 ++- iterators.html | 10 +- native-datatypes.html | 50 +++--- porting-code-to-python-3-with-2to3.html | 56 +++--- publish | 2 +- refactoring.html | 69 ++++---- regular-expressions.html | 8 +- special-method-names.html | 70 ++++---- strings.html | 44 ++--- table-of-contents.html | 2 +- unit-testing.html | 81 +++------ whats-new.html | 4 +- where-to-go-from-here.html | 16 +- xml.html | 185 ++++++++++---------- your-first-python-program.html | 34 ++-- 64 files changed, 724 insertions(+), 764 deletions(-) diff --git a/about.html b/about.html index 1a78ac3..a66eea9 100644 --- a/about.html +++ b/about.html @@ -4,7 +4,7 @@ About the book - Dive Into Python 3 diff --git a/advanced-classes.html b/advanced-classes.html index 994115c..eff6d0a 100644 --- a/advanced-classes.html +++ b/advanced-classes.html @@ -163,7 +163,7 @@ class OrderedDict(dict, collections.MutableMapping):

Implementing Fractions

-

© 2001–9 Mark Pilgrim diff --git a/advanced-iterators.html b/advanced-iterators.html index 5ff1a61..2a2729b 100644 --- a/advanced-iterators.html +++ b/advanced-iterators.html @@ -391,7 +391,7 @@ for guess in itertools.permutations(digits, len(characters)):

Python strings have many methods. You learned about some of those methods in the Strings chapter: lower(), count(), and format(). Now I want to introduce you to a powerful but little-known string manipulation technique: the translate() method.

->>> translation_table = {ord("A"): ord("O")}  
+>>> translation_table = {ord('A'): ord('O')}  
 >>> translation_table                         
 {65: 79}
 >>> 'MARK'.translate(translation_table)       
@@ -414,7 +414,7 @@ for guess in itertools.permutations(digits, len(characters)):
 >>> translation_table = dict(zip(characters, guess))     
 >>> translation_table
 {68: 55, 69: 53, 77: 49, 78: 54, 79: 48, 82: 56, 83: 57, 89: 50}
->>> "SEND + MORE == MONEY".translate(translation_table)  
+>>> 'SEND + MORE == MONEY'.translate(translation_table)  
 '9567 + 1085 == 10652'
  1. Using a generator expression, we quickly compute the byte values for each character in a string. characters is an example of the value of sorted_characters in the alphametics.solve() function. @@ -490,7 +490,7 @@ for guess in itertools.permutations(digits, len(characters)):
  2. Don’t do this either.
-

eval() is EVIL +

eval() is EVIL

Well, the evil part is evaluating arbitrary expressions from untrusted sources. You should only use eval() on trusted input. Of course, the trick is figuring out what’s “trusted.” But here’s something I know for certain: you should NOT take this alphametics solver and put it on the internet as a fun little web service. Don’t make the mistake of thinking, “Gosh, the function does a lot of string manipulation before getting a string to evaluate; I can’t imagine how someone could exploit that.” Someone WILL figure out how to sneak nasty executable code past all that string manipulation (stranger things have happened), and then you can kiss your server goodbye. @@ -591,8 +591,9 @@ NameError: name '__import__' is not defined

Many, many thanks to Raymond Hettinger for agreeing to relicense his code so I could port it to Python 3 and use it as the basis for this chapter. -

+

© 2001–9 Mark Pilgrim diff --git a/case-study-porting-chardet-to-python-3.html b/case-study-porting-chardet-to-python-3.html index 799e67b..ded5b85 100644 --- a/case-study-porting-chardet-to-python-3.html +++ b/case-study-porting-chardet-to-python-3.html @@ -50,19 +50,19 @@ del{background:#f87}

The main entry point for the detection algorithm is universaldetector.py, which has one class, UniversalDetector. (You might think the main entry point is the detect function in chardet/__init__.py, but that’s really just a convenience function that creates a UniversalDetector object, calls it, and returns its result.)

There are 5 categories of encodings that UniversalDetector handles:

    -
  1. UTF-n with a BOM. This includes UTF-8, both BE and LE variants of UTF-16, and all 4 byte-order variants of UTF-32. +
  2. UTF-n with a Byte Order Mark (BOM). This includes UTF-8, both Big-Endian and Little-Endian variants of UTF-16, and all 4 byte-order variants of UTF-32.
  3. Escaped encodings, which are entirely 7-bit ASCII compatible, where non-ASCII characters start with an escape sequence. Examples: ISO-2022-JP (Japanese) and HZ-GB-2312 (Chinese). -
  4. Multi-byte encodings, where each character is represented by a variable number of bytes. Examples: Big5 (Chinese), SHIFT_JIS (Japanese), EUC-KR (Korean), and UTF-8 without a BOM. +
  5. Multi-byte encodings, where each character is represented by a variable number of bytes. Examples: Big5 (Chinese), SHIFT_JIS (Japanese), EUC-KR (Korean), and UTF-8 without a BOM.
  6. Single-byte encodings, where each character is represented by one byte. Examples: KOI8-R (Russian), windows-1255 (Hebrew), and TIS-620 (Thai).
  7. windows-1252, which is used primarily on Microsoft Windows by middle managers who wouldn’t know a character encoding from a hole in the ground.
-

UTF-n With A BOM

-

If the text starts with a BOM, we can reasonably assume that the text is encoded in UTF-8, UTF-16, or UTF-32. (The BOM will tell us exactly which one; that’s what it’s for.) This is handled inline in UniversalDetector, which returns the result immediately without any further processing. +

UTF-n With A BOM

+

If the text starts with a BOM, we can reasonably assume that the text is encoded in UTF-8, UTF-16, or UTF-32. (The BOM will tell us exactly which one; that’s what it’s for.) This is handled inline in UniversalDetector, which returns the result immediately without any further processing.

Escaped Encodings

If the text contains a recognizable escape sequence that might indicate an escaped encoding, UniversalDetector creates an EscCharSetProber (defined in escprober.py) and feeds it the text.

EscCharSetProber creates a series of state machines, based on models of HZ-GB-2312, ISO-2022-CN, ISO-2022-JP, and ISO-2022-KR (defined in escsm.py). EscCharSetProber feeds the text to each of these state machines, one byte at a time. If any state machine ends up uniquely identifying the encoding, EscCharSetProber immediately returns the positive result to UniversalDetector, which returns it to the caller. If any state machine hits an illegal sequence, it is dropped and processing continues with the other state machines.

Multi-Byte Encodings

-

Assuming no BOM, UniversalDetector checks whether the text contains any high-bit characters. If so, it creates a series of “probers” for detecting multi-byte encodings, single-byte encodings, and as a last resort, windows-1252. +

Assuming no BOM, UniversalDetector checks whether the text contains any high-bit characters. If so, it creates a series of “probers” for detecting multi-byte encodings, single-byte encodings, and as a last resort, windows-1252.

The multi-byte encoding prober, MBCSGroupProber (defined in mbcsgroupprober.py), is really just a shell that manages a group of other probers, one for each multi-byte encoding: Big5, GB2312, EUC-TW, EUC-KR, EUC-JP, SHIFT_JIS, and UTF-8. MBCSGroupProber feeds the text to each of these encoding-specific probers and checks the results. If a prober reports that it has found an illegal byte sequence, it is dropped from further processing (so that, for instance, any subsequent calls to UniversalDetector.feed() will skip that prober). If a prober reports that it is reasonably confident that it has detected the encoding, MBCSGroupProber reports this positive result to UniversalDetector, which reports the result to the caller.

Most of the multi-byte encoding probers are inherited from MultiByteCharSetProber (defined in mbcharsetprober.py), and simply hook up the appropriate state machine and distribution analyzer and let MultiByteCharSetProber do the rest of the work. MultiByteCharSetProber runs the text through the encoding-specific state machine, one byte at a time, to look for byte sequences that would indicate a conclusive positive or negative result. At the same time, MultiByteCharSetProber feeds the text to an encoding-specific distribution analyzer.

The distribution analyzers (each defined in chardistribution.py) use language-specific models of which characters are used most frequently. Once MultiByteCharSetProber has fed enough text to the distribution analyzer, it calculates a confidence rating based on the number of frequently-used characters, the total number of characters, and a language-specific distribution ratio. If the confidence is high enough, MultiByteCharSetProber returns the result to MBCSGroupProber, which returns it to UniversalDetector, which returns it to the caller. @@ -1126,7 +1126,7 @@ tests\Big5\0804.blogspot.com.xml File "C:\home\chardet\chardet\latin1prober.py", line 126, in get_confidence total = reduce(operator.add, self._mFreqCounter) NameError: global name 'reduce' is not defined -

According to the official What’s New In Python 3.0 guide, the reduce() function has been moved out of the global namespace and into the functools module. Quoting the guide: “Use functools.reduce() if you really need it; however, 99 percent of the time an explicit for loop is more readable.” You can read more about the decision from Guido van Rossum’s weblog: The fate of reduce() in Python 3000. +

According to the official What’s New In Python 3.0 guide, the reduce() function has been moved out of the global namespace and into the functools module. Quoting the guide: “Use functools.reduce() if you really need it; however, 99 percent of the time an explicit for loop is more readable.” You can read more about the decision from Guido van Rossum’s weblog: The fate of reduce() in Python 3000.

def get_confidence(self):
     if self.get_state() == constants.eNotMe:
         return 0.01
@@ -1192,7 +1192,7 @@ tests\EUC-JP\arclamp.jp.xml                                  EUC-JP with confide
 
  • Test cases are essential. Don’t port anything without them. Don’t even try. The only reason I have any confidence at all that chardet works in Python 3 is because I had a test suite that exercised every line of code in the entire library. I never would have found half of these problems with manual spot-checking. -

    © 2001–9 Mark Pilgrim diff --git a/dip3.css b/dip3.css index 4a7a553..c5326e8 100644 --- a/dip3.css +++ b/dip3.css @@ -28,21 +28,21 @@ POSSIBILITY OF SUCH DAMAGE. Classname Legend -.w = "widgets" = wrapper block for hide/open/download links dynamically inserted into code listings -.b = "block" = internal block dynamically inserted into code listings -.d = "download" = download link for code listings -.p = "prompt" = command-line or interactive shell prompt within code listings -.q = "quote" = quote at beginning of each chapter -.f = "fancy" = first paragraph of each chapter (gets a fancy drop-cap) -.c = "centered" = centered footer text (also clears floats) -.a = "asterism" = section break +.w = "widgets" = wrapper block for hide/open/download links dynamically inserted into code listings +.b = "block" = internal block dynamically inserted into code listings +.d = "download" = download link for code listings +.p = "prompt" = command-line or interactive shell prompt within code listings +.q = "quote" = quote at beginning of each chapter +.f = "fancy" = first paragraph of each chapter (gets a fancy drop-cap) +.c = "centered" = centered footer text (also clears floats) +.a = "asterism" = section break +.v = "navigation" = prev/next navigation links (not breadcrumbs) .nm = "no mobile" = hide this section on mobile devices .nd = "no decoration" = hide the widgets on this code block .note = "note/caution/important" = indented block for tips/gotchas/language comparisons .baa = "best available ampersand" = wrapper block for ampersands -.nav = "navigation" = prev/next navigation links (not breadcrumbs) Acknowledgements & Inspirations @@ -273,18 +273,18 @@ aside { } /* previous/next navigation links */ -.nav a { +.v a { text-decoration: none; border: 0; display: block; } -.nav a:first-child { +.v a:first-child { float: left; } -.nav a:last-child { +.v a:last-child { float: right; } -.nav span { +.v span { font-size: 1000%; line-height: 1; margin: 0; diff --git a/examples/alphametics.py b/examples/alphametics.py index 2c9c515..fbaae83 100644 --- a/examples/alphametics.py +++ b/examples/alphametics.py @@ -1,8 +1,8 @@ -"""Find solutions to alphametic equations. +'''Find solutions to alphametic equations. >>> alphametics.solve('SEND + MORE == MONEY') '9567 + 1085 == 10652' -""" +''' import re import itertools diff --git a/examples/alphameticstest.py b/examples/alphameticstest.py index 7f66dca..7ff704c 100644 --- a/examples/alphameticstest.py +++ b/examples/alphameticstest.py @@ -3,34 +3,34 @@ import unittest class KnownValues(unittest.TestCase): def test_out(self): - """TO + GO == OUT""" - self.assertEqual(solve("TO + GO == OUT"), "21 + 81 == 102") + '''TO + GO == OUT''' + self.assertEqual(solve('TO + GO == OUT'), '21 + 81 == 102') def test_too(self): - """I + DID == TOO""" - self.assertEqual(solve("I + DID == TOO"), "9 + 191 == 200") + '''I + DID == TOO''' + self.assertEqual(solve('I + DID == TOO'), '9 + 191 == 200') def test_mom(self): - """AS + A == MOM""" - self.assertEqual(solve("AS + A == MOM"), "92 + 9 == 101") + '''AS + A == MOM''' + self.assertEqual(solve('AS + A == MOM'), '92 + 9 == 101') def test_best(self): - """HES + THE == BEST""" - self.assertEqual(solve("HES + THE == BEST"), "426 + 842 == 1268") + '''HES + THE == BEST''' + self.assertEqual(solve('HES + THE == BEST'), '426 + 842 == 1268') def test_late(self): - """NO + NO + TOO == LATE""" - self.assertEqual(solve("NO + NO + TOO == LATE"), "74 + 74 + 944 == 1092") + '''NO + NO + TOO == LATE''' + self.assertEqual(solve('NO + NO + TOO == LATE'), '74 + 74 + 944 == 1092') def test_onze(self): - """UN + UN + NEUF == ONZE""" - self.assertEqual(solve("UN + UN + NEUF == ONZE"), "81 + 81 + 1987 == 2149") + '''UN + UN + NEUF == ONZE''' + self.assertEqual(solve('UN + UN + NEUF == ONZE'), '81 + 81 + 1987 == 2149') def test_deux(self): - """UN + DEUX + DEUX + DEUX + DEUX == NEUF""" - self.assertEqual(solve("UN + DEUX + DEUX + DEUX + DEUX == NEUF"), "25 + 1326 + 1326 + 1326 + 1326 == 5329") + '''UN + DEUX + DEUX + DEUX + DEUX == NEUF''' + self.assertEqual(solve('UN + DEUX + DEUX + DEUX + DEUX == NEUF'), '25 + 1326 + 1326 + 1326 + 1326 == 5329') -if __name__ == "__main__": +if __name__ == '__main__': unittest.main() # Copyright (c) 2009, Mark Pilgrim, All rights reserved. diff --git a/examples/feed-broken.xml b/examples/feed-broken.xml index 88f7517..15bc53a 100644 --- a/examples/feed-broken.xml +++ b/examples/feed-broken.xml @@ -1,25 +1,25 @@ - - + + dive into … currently between addictions tag:diveintomark.org,2001-07-29:/ 2009-03-27T21:56:07Z - + Mark http://diveintomark.org/ Dive into history, 2009 edition - + tag:diveintomark.org,2009-03-27:/archives/20090327172042 2009-03-27T21:56:07Z 2009-03-27T17:20:42Z - - - -

    Putting an entire chapter on one page sounds + + + + Putting an entire chapter on one page sounds bloated, but consider this &mdash; my longest chapter so far would be 75 printed pages, and it loads in under 5 seconds&hellip; On dialup. @@ -30,13 +30,13 @@ http://diveintomark.org/ Accessibility is a harsh mistress - + tag:diveintomark.org,2009-03-21:/archives/20090321200928 2009-03-22T01:05:37Z 2009-03-21T20:09:28Z - - The accessibility orthodoxy does not permit people to + + The accessibility orthodoxy does not permit people to question the value of features that are rarely useful and rarely used. @@ -44,20 +44,20 @@ Mark A gentle introduction to video encoding, part 1: container formats - + tag:diveintomark.org,2008-12-18:/archives/20081218155422 2009-01-11T19:39:22Z 2008-12-18T15:54:22Z - - - - - - - - - These notes will eventually become part of a + + + + + + + + + These notes will eventually become part of a tech talk on video encoding. diff --git a/examples/feed-ns0.xml b/examples/feed-ns0.xml index 9590116..7810882 100644 --- a/examples/feed-ns0.xml +++ b/examples/feed-ns0.xml @@ -1,25 +1,25 @@ - - + + dive into mark currently between addictions tag:diveintomark.org,2001-07-29:/ 2009-03-27T21:56:07Z - + Mark http://diveintomark.org/ Dive into history, 2009 edition - + tag:diveintomark.org,2009-03-27:/archives/20090327172042 2009-03-27T21:56:07Z 2009-03-27T17:20:42Z - - - - Putting an entire chapter on one page sounds + + + + Putting an entire chapter on one page sounds bloated, but consider this &mdash; my longest chapter so far would be 75 printed pages, and it loads in under 5 seconds&hellip; On dialup. @@ -30,13 +30,13 @@ http://diveintomark.org/ Accessibility is a harsh mistress - + tag:diveintomark.org,2009-03-21:/archives/20090321200928 2009-03-22T01:05:37Z 2009-03-21T20:09:28Z - - The accessibility orthodoxy does not permit people to + + The accessibility orthodoxy does not permit people to question the value of features that are rarely useful and rarely used. @@ -44,20 +44,20 @@ Mark A gentle introduction to video encoding, part 1: container formats - + tag:diveintomark.org,2008-12-18:/archives/20081218155422 2009-01-11T19:39:22Z 2008-12-18T15:54:22Z - - - - - - - - - These notes will eventually become part of a + + + + + + + + + These notes will eventually become part of a tech talk on video encoding. diff --git a/examples/feed.xml b/examples/feed.xml index b637957..bcdda81 100644 --- a/examples/feed.xml +++ b/examples/feed.xml @@ -1,25 +1,25 @@ - - + + dive into mark currently between addictions tag:diveintomark.org,2001-07-29:/ 2009-03-27T21:56:07Z - + Mark http://diveintomark.org/ Dive into history, 2009 edition - + tag:diveintomark.org,2009-03-27:/archives/20090327172042 2009-03-27T21:56:07Z 2009-03-27T17:20:42Z - - - - Putting an entire chapter on one page sounds + + + + Putting an entire chapter on one page sounds bloated, but consider this &mdash; my longest chapter so far would be 75 printed pages, and it loads in under 5 seconds&hellip; On dialup. @@ -30,13 +30,13 @@ http://diveintomark.org/ Accessibility is a harsh mistress - + tag:diveintomark.org,2009-03-21:/archives/20090321200928 2009-03-22T01:05:37Z 2009-03-21T20:09:28Z - - The accessibility orthodoxy does not permit people to + + The accessibility orthodoxy does not permit people to question the value of features that are rarely useful and rarely used. @@ -44,20 +44,20 @@ Mark A gentle introduction to video encoding, part 1: container formats - + tag:diveintomark.org,2008-12-18:/archives/20081218155422 2009-01-11T19:39:22Z 2008-12-18T15:54:22Z - - - - - - - - - These notes will eventually become part of a + + + + + + + + + These notes will eventually become part of a tech talk on video encoding. diff --git a/examples/fibonacci.py b/examples/fibonacci.py index 9463ff0..5fd70cf 100644 --- a/examples/fibonacci.py +++ b/examples/fibonacci.py @@ -1,4 +1,4 @@ -"""Fibonacci generator""" +'''Fibonacci generator''' def fib(max): a, b = 0, 1 diff --git a/examples/fibonacci2.py b/examples/fibonacci2.py index bfe611c..f9df554 100644 --- a/examples/fibonacci2.py +++ b/examples/fibonacci2.py @@ -1,7 +1,7 @@ -"""Fibonacci iterator""" +'''Fibonacci iterator''' class Fib: - """iterator that yields numbers in the Fibonacci sequence""" + '''iterator that yields numbers in the Fibonacci sequence''' def __init__(self, max): self.max = max diff --git a/examples/humansize.py b/examples/humansize.py index 2f40c82..f27f602 100644 --- a/examples/humansize.py +++ b/examples/humansize.py @@ -1,4 +1,4 @@ -"""Convert file sizes to human-readable form. +'''Convert file sizes to human-readable form. Available functions: approximate_size(size, a_kilobyte_is_1024_bytes) @@ -10,13 +10,13 @@ Examples: >>> approximate_size(1000, False) '1.0 KB' -""" +''' SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']} def approximate_size(size, a_kilobyte_is_1024_bytes=True): - """Convert a file size to human-readable form. + '''Convert a file size to human-readable form. Keyword arguments: size -- file size in bytes @@ -25,7 +25,7 @@ def approximate_size(size, a_kilobyte_is_1024_bytes=True): Returns: string - """ + ''' if size < 0: raise ValueError('number must be non-negative') @@ -33,11 +33,11 @@ def approximate_size(size, a_kilobyte_is_1024_bytes=True): for suffix in SUFFIXES[multiple]: size /= multiple if size < multiple: - return "{0:.1f} {1}".format(size, suffix) + return '{0:.1f} {1}'.format(size, suffix) raise ValueError('number too large') -if __name__ == "__main__": +if __name__ == '__main__': print(approximate_size(1000000000000, False)) print(approximate_size(1000000000000)) diff --git a/examples/plural1.py b/examples/plural1.py index f3b1dcf..85099e8 100644 --- a/examples/plural1.py +++ b/examples/plural1.py @@ -1,9 +1,9 @@ -"""Pluralize English nouns (stage 1) +'''Pluralize English nouns (stage 1) Command line usage: $ python3 plural.py noun nouns -""" +''' import re diff --git a/examples/plural2.py b/examples/plural2.py index cae0e4a..1852804 100644 --- a/examples/plural2.py +++ b/examples/plural2.py @@ -1,9 +1,9 @@ -"""Pluralize English nouns (stage 2) +'''Pluralize English nouns (stage 2) Command line usage: $ python plural2.py noun nouns -""" +''' import re diff --git a/examples/plural3.py b/examples/plural3.py index ce407db..4029495 100644 --- a/examples/plural3.py +++ b/examples/plural3.py @@ -1,9 +1,9 @@ -"""Pluralize English nouns (stage 3) +'''Pluralize English nouns (stage 3) Command line usage: $ python plural3.py noun nouns -""" +''' import re diff --git a/examples/plural4.py b/examples/plural4.py index 3b7bfaf..560a5dd 100644 --- a/examples/plural4.py +++ b/examples/plural4.py @@ -1,9 +1,9 @@ -"""Pluralize English nouns (stage 4) +'''Pluralize English nouns (stage 4) Command line usage: $ python plural4.py noun nouns -""" +''' import re diff --git a/examples/plural5.py b/examples/plural5.py index 99c3321..7d28b2f 100644 --- a/examples/plural5.py +++ b/examples/plural5.py @@ -1,9 +1,9 @@ -"""Pluralize English nouns (stage 5) +'''Pluralize English nouns (stage 5) Command line usage: $ python plural5.py noun nouns -""" +''' import re diff --git a/examples/plural6.py b/examples/plural6.py index bb68394..f29293b 100644 --- a/examples/plural6.py +++ b/examples/plural6.py @@ -1,9 +1,9 @@ -"""Pluralize English nouns (stage 6) +'''Pluralize English nouns (stage 6) Command line usage: $ python plural6.py noun nouns -""" +''' import re diff --git a/examples/pluraltest1.py b/examples/pluraltest1.py index 7ded294..86538ae 100644 --- a/examples/pluraltest1.py +++ b/examples/pluraltest1.py @@ -1,11 +1,11 @@ -"""Unit test for plural1.py""" +'''Unit test for plural1.py''' import plural1 import unittest class KnownValues(unittest.TestCase): def test_sxz(self): - "words ending in S, X, and Z" + 'words ending in S, X, and Z' nouns = { 'bass': 'basses', 'bus': 'buses', @@ -21,7 +21,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural1.plural(singular), plural) def test_h(self): - "words ending in H" + 'words ending in H' nouns = { 'coach': 'coaches', 'glitch': 'glitches', @@ -34,7 +34,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural1.plural(singular), plural) def test_y(self): - "words ending in Y" + 'words ending in Y' nouns = { 'utility': 'utilities', 'vacancy': 'vacancies', @@ -45,7 +45,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural1.plural(singular), plural) def test_default(self): - "unexceptional words" + 'unexceptional words' nouns = { 'papaya': 'papayas', 'whip': 'whips', @@ -54,7 +54,7 @@ class KnownValues(unittest.TestCase): for singular, plural in nouns.items(): self.assertEqual(plural1.plural(singular), plural) -if __name__ == "__main__": +if __name__ == '__main__': unittest.main() # Copyright (c) 2009, Mark Pilgrim, All rights reserved. diff --git a/examples/pluraltest2.py b/examples/pluraltest2.py index 54aedf6..f6163c2 100644 --- a/examples/pluraltest2.py +++ b/examples/pluraltest2.py @@ -1,11 +1,11 @@ -"""Unit test for plural2.py""" +'''Unit test for plural2.py''' import plural2 import unittest class KnownValues(unittest.TestCase): def test_sxz(self): - "words ending in S, X, and Z" + 'words ending in S, X, and Z' nouns = { 'bass': 'basses', 'bus': 'buses', @@ -21,7 +21,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural2.plural(singular), plural) def test_h(self): - "words ending in H" + 'words ending in H' nouns = { 'coach': 'coaches', 'glitch': 'glitches', @@ -34,7 +34,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural2.plural(singular), plural) def test_y(self): - "words ending in Y" + 'words ending in Y' nouns = { 'utility': 'utilities', 'vacancy': 'vacancies', @@ -45,7 +45,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural2.plural(singular), plural) def test_default(self): - "unexceptional words" + 'unexceptional words' nouns = { 'papaya': 'papayas', 'whip': 'whips', @@ -54,7 +54,7 @@ class KnownValues(unittest.TestCase): for singular, plural in nouns.items(): self.assertEqual(plural2.plural(singular), plural) -if __name__ == "__main__": +if __name__ == '__main__': unittest.main() # Copyright (c) 2009, Mark Pilgrim, All rights reserved. diff --git a/examples/pluraltest3.py b/examples/pluraltest3.py index 7c0d7d1..96b802e 100644 --- a/examples/pluraltest3.py +++ b/examples/pluraltest3.py @@ -1,11 +1,11 @@ -"""Unit test for plural1.py""" +'''Unit test for plural1.py''' import plural3 import unittest class KnownValues(unittest.TestCase): def test_sxz(self): - "words ending in S, X, and Z" + 'words ending in S, X, and Z' nouns = { 'bass': 'basses', 'bus': 'buses', @@ -21,7 +21,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural3.plural(singular), plural) def test_h(self): - "words ending in H" + 'words ending in H' nouns = { 'coach': 'coaches', 'glitch': 'glitches', @@ -34,7 +34,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural3.plural(singular), plural) def test_y(self): - "words ending in Y" + 'words ending in Y' nouns = { 'utility': 'utilities', 'vacancy': 'vacancies', @@ -45,7 +45,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural3.plural(singular), plural) def test_default(self): - "unexceptional words" + 'unexceptional words' nouns = { 'papaya': 'papayas', 'whip': 'whips', @@ -54,7 +54,7 @@ class KnownValues(unittest.TestCase): for singular, plural in nouns.items(): self.assertEqual(plural3.plural(singular), plural) -if __name__ == "__main__": +if __name__ == '__main__': unittest.main() # Copyright (c) 2009, Mark Pilgrim, All rights reserved. diff --git a/examples/pluraltest4.py b/examples/pluraltest4.py index 608c56f..49399a4 100644 --- a/examples/pluraltest4.py +++ b/examples/pluraltest4.py @@ -1,11 +1,11 @@ -"""Unit test for plural1.py""" +'''Unit test for plural1.py''' import plural4 import unittest class KnownValues(unittest.TestCase): def test_sxz(self): - "words ending in S, X, and Z" + 'words ending in S, X, and Z' nouns = { 'bass': 'basses', 'bus': 'buses', @@ -21,7 +21,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural4.plural(singular), plural) def test_h(self): - "words ending in H" + 'words ending in H' nouns = { 'coach': 'coaches', 'glitch': 'glitches', @@ -34,7 +34,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural4.plural(singular), plural) def test_y(self): - "words ending in Y" + 'words ending in Y' nouns = { 'utility': 'utilities', 'vacancy': 'vacancies', @@ -45,7 +45,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural4.plural(singular), plural) def test_default(self): - "unexceptional words" + 'unexceptional words' nouns = { 'papaya': 'papayas', 'whip': 'whips', @@ -54,7 +54,7 @@ class KnownValues(unittest.TestCase): for singular, plural in nouns.items(): self.assertEqual(plural4.plural(singular), plural) -if __name__ == "__main__": +if __name__ == '__main__': unittest.main() # Copyright (c) 2009, Mark Pilgrim, All rights reserved. diff --git a/examples/pluraltest5.py b/examples/pluraltest5.py index 488fda8..cde16b9 100644 --- a/examples/pluraltest5.py +++ b/examples/pluraltest5.py @@ -1,11 +1,11 @@ -"""Unit test for plural5.py""" +'''Unit test for plural5.py''' import plural5 import unittest class KnownValues(unittest.TestCase): def test_sxz(self): - "words ending in S, X, and Z" + 'words ending in S, X, and Z' nouns = { 'bass': 'basses', 'bus': 'buses', @@ -21,7 +21,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural5.plural(singular), plural) def test_h(self): - "words ending in H" + 'words ending in H' nouns = { 'coach': 'coaches', 'glitch': 'glitches', @@ -34,7 +34,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural5.plural(singular), plural) def test_y(self): - "words ending in Y" + 'words ending in Y' nouns = { 'utility': 'utilities', 'vacancy': 'vacancies', @@ -45,7 +45,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural5.plural(singular), plural) def test_default(self): - "unexceptional words" + 'unexceptional words' nouns = { 'papaya': 'papayas', 'whip': 'whips', @@ -54,10 +54,7 @@ class KnownValues(unittest.TestCase): for singular, plural in nouns.items(): self.assertEqual(plural5.plural(singular), plural) -if __name__ == "__main__": - unittest.main() - -if __name__ == "__main__": +if __name__ == '__main__': unittest.main() # Copyright (c) 2009, Mark Pilgrim, All rights reserved. diff --git a/examples/pluraltest6.py b/examples/pluraltest6.py index 37ac492..7eee2f1 100644 --- a/examples/pluraltest6.py +++ b/examples/pluraltest6.py @@ -1,11 +1,11 @@ -"""Unit test for plural6.py""" +'''Unit test for plural6.py''' import plural6 import unittest class KnownValues(unittest.TestCase): def test_sxz(self): - "words ending in S, X, and Z" + 'words ending in S, X, and Z' nouns = { 'bass': 'basses', 'bus': 'buses', @@ -21,7 +21,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural6.plural(singular), plural) def test_h(self): - "words ending in H" + 'words ending in H' nouns = { 'coach': 'coaches', 'glitch': 'glitches', @@ -34,7 +34,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural6.plural(singular), plural) def test_y(self): - "words ending in Y" + 'words ending in Y' nouns = { 'utility': 'utilities', 'vacancy': 'vacancies', @@ -45,7 +45,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural6.plural(singular), plural) def test_ouce(self): - "words ending in OUSE" + 'words ending in OUSE' nouns = { 'mouse': 'mice', 'louse': 'lice' @@ -54,7 +54,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural6.plural(singular), plural) def test_child(self): - "special case: child" + 'special case: child' nouns = { 'child': 'children' } @@ -62,7 +62,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural6.plural(singular), plural) def test_oot(self): - "special case: foot" + 'special case: foot' nouns = { 'foot': 'feet' } @@ -70,7 +70,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural6.plural(singular), plural) def test_ooth(self): - "words ending in OOTH" + 'words ending in OOTH' nouns = { 'booth': 'booths', 'tooth': 'teeth' @@ -79,7 +79,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural6.plural(singular), plural) def test_f_ves(self): - "words ending in F that become VES" + 'words ending in F that become VES' nouns = { 'leaf': 'leaves', 'loaf': 'loaves' @@ -88,7 +88,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural6.plural(singular), plural) def test_sis(self): - "words ending in SIS" + 'words ending in SIS' nouns = { 'thesis': 'theses' } @@ -96,7 +96,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural6.plural(singular), plural) def test_man(self): - "words ending in MAN" + 'words ending in MAN' nouns = { 'man': 'men', 'mailman': 'mailmen', @@ -107,7 +107,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural6.plural(singular), plural) def test_ife(self): - "words ending in IFE" + 'words ending in IFE' nouns = { 'knife': 'knives', 'wife': 'wives', @@ -117,7 +117,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural6.plural(singular), plural) def test_eau(self): - "words ending in EAU" + 'words ending in EAU' nouns = { 'tableau': 'tableaux' } @@ -125,7 +125,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural6.plural(singular), plural) def test_elf(self): - "words ending in ELF" + 'words ending in ELF' nouns = { 'elf': 'elves', 'shelf': 'shelves', @@ -136,7 +136,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural6.plural(singular), plural) def test_same(self): - "words that are their own plural" + 'words that are their own plural' nouns = { 'sheep': 'sheep', 'deer': 'deer', @@ -150,7 +150,7 @@ class KnownValues(unittest.TestCase): self.assertEqual(plural6.plural(singular), plural) def test_default(self): - "unexceptional words" + 'unexceptional words' nouns = { 'papaya': 'papayas', 'whip': 'whips', @@ -159,10 +159,7 @@ class KnownValues(unittest.TestCase): for singular, plural in nouns.items(): self.assertEqual(plural6.plural(singular), plural) -if __name__ == "__main__": - unittest.main() - -if __name__ == "__main__": +if __name__ == '__main__': unittest.main() # Copyright (c) 2009, Mark Pilgrim, All rights reserved. diff --git a/examples/roman1.py b/examples/roman1.py index 49a99cf..58cde03 100644 --- a/examples/roman1.py +++ b/examples/roman1.py @@ -1,9 +1,9 @@ -"""Convert to and from Roman numerals +'''Convert to and from Roman numerals -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' roman_numeral_map = (('M', 1000), ('CM', 900), @@ -20,8 +20,8 @@ roman_numeral_map = (('M', 1000), ('I', 1)) def to_roman(n): - """convert integer to Roman numeral""" - result = "" + '''convert integer to Roman numeral''' + result = '' for numeral, integer in roman_numeral_map: while n >= integer: result += numeral diff --git a/examples/roman10.py b/examples/roman10.py index 0efdc45..a6f23fb 100644 --- a/examples/roman10.py +++ b/examples/roman10.py @@ -1,9 +1,9 @@ -"""Convert to and from Roman numerals +'''Convert to and from Roman numerals -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' class OutOfRangeError(ValueError): pass class NotIntegerError(ValueError): pass @@ -27,26 +27,26 @@ to_roman_table = [ None ] from_roman_table = {} def to_roman(n): - """convert integer to Roman numeral""" + '''convert integer to Roman numeral''' if not (0 < n < 5000): - raise OutOfRangeError("number out of range (must be 1..4999)") + raise OutOfRangeError('number out of range (must be 1..4999)') if int(n) != n: - raise NotIntegerError("non-integers can not be converted") + raise NotIntegerError('non-integers can not be converted') return to_roman_table[n] def from_roman(s): - """convert Roman numeral to integer""" + '''convert Roman numeral to integer''' if not isinstance(s, str): - raise InvalidRomanNumeralError("Input must be a string") + raise InvalidRomanNumeralError('Input must be a string') if not s: - raise InvalidRomanNumeralError("Input can not be blank") + raise InvalidRomanNumeralError('Input can not be blank') if s not in from_roman_table: - raise InvalidRomanNumeralError("Invalid Roman numeral: {0}".format(s)) + raise InvalidRomanNumeralError('Invalid Roman numeral: {0}'.format(s)) return from_roman_table[s] def build_lookup_tables(): def to_roman(n): - result = "" + result = '' for numeral, integer in roman_numeral_map: if n >= integer: result = numeral diff --git a/examples/roman2.py b/examples/roman2.py index 5c0bcea..50dee99 100644 --- a/examples/roman2.py +++ b/examples/roman2.py @@ -1,9 +1,9 @@ -"""Convert to and from Roman numerals +'''Convert to and from Roman numerals -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' class OutOfRangeError(ValueError): pass @@ -22,11 +22,11 @@ roman_numeral_map = (('M', 1000), ('I', 1)) def to_roman(n): - """convert integer to Roman numeral""" + '''convert integer to Roman numeral''' if n > 3999: - raise OutOfRangeError("number out of range (must be less than 3999)") + raise OutOfRangeError('number out of range (must be less than 3999)') - result = "" + result = '' for numeral, integer in roman_numeral_map: while n >= integer: result += numeral diff --git a/examples/roman3.py b/examples/roman3.py index 9470362..561018e 100644 --- a/examples/roman3.py +++ b/examples/roman3.py @@ -1,9 +1,9 @@ -"""Convert to and from Roman numerals +'''Convert to and from Roman numerals -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' class OutOfRangeError(ValueError): pass roman_numeral_map = (('M', 1000), @@ -21,11 +21,11 @@ roman_numeral_map = (('M', 1000), ('I', 1)) def to_roman(n): - """convert integer to Roman numeral""" + '''convert integer to Roman numeral''' if not (0 < n < 4000): - raise OutOfRangeError("number out of range (must be 0..3999)") + raise OutOfRangeError('number out of range (must be 0..3999)') - result = "" + result = '' for numeral, integer in roman_numeral_map: while n >= integer: result += numeral diff --git a/examples/roman4.py b/examples/roman4.py index 31b6bdc..cfd6de7 100644 --- a/examples/roman4.py +++ b/examples/roman4.py @@ -1,9 +1,9 @@ -"""Convert to and from Roman numerals +'''Convert to and from Roman numerals -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' class OutOfRangeError(ValueError): pass class NotIntegerError(ValueError): pass @@ -22,13 +22,13 @@ roman_numeral_map = (('M', 1000), ('I', 1)) def to_roman(n): - """convert integer to Roman numeral""" + '''convert integer to Roman numeral''' if not (0 < n < 4000): - raise OutOfRangeError("number out of range (must be 0..3999)") + raise OutOfRangeError('number out of range (must be 0..3999)') if not isinstance(n, int): - raise NotIntegerError("non-integers can not be converted") + raise NotIntegerError('non-integers can not be converted') - result = "" + result = '' for numeral, integer in roman_numeral_map: while n >= integer: result += numeral diff --git a/examples/roman5.py b/examples/roman5.py index b564510..a230672 100644 --- a/examples/roman5.py +++ b/examples/roman5.py @@ -1,9 +1,9 @@ -"""Convert to and from Roman numerals +'''Convert to and from Roman numerals -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' class OutOfRangeError(ValueError): pass class NotIntegerError(ValueError): pass @@ -22,13 +22,13 @@ roman_numeral_map = (('M', 1000), ('I', 1)) def to_roman(n): - """convert integer to Roman numeral""" + '''convert integer to Roman numeral''' if not (0 < n < 4000): - raise OutOfRangeError("number out of range (must be 0..3999)") + raise OutOfRangeError('number out of range (must be 0..3999)') if not isinstance(n, int): - raise NotIntegerError("non-integers can not be converted") + raise NotIntegerError('non-integers can not be converted') - result = "" + result = '' for numeral, integer in roman_numeral_map: while n >= integer: result += numeral @@ -36,7 +36,7 @@ def to_roman(n): return result def from_roman(s): - """convert Roman numeral to integer""" + '''convert Roman numeral to integer''' result = 0 index = 0 for numeral, integer in roman_numeral_map: diff --git a/examples/roman6.py b/examples/roman6.py index f8bd5c4..512e7ff 100644 --- a/examples/roman6.py +++ b/examples/roman6.py @@ -1,9 +1,9 @@ -"""Convert to and from Roman numerals +'''Convert to and from Roman numerals -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' import re class OutOfRangeError(ValueError): pass @@ -24,7 +24,7 @@ roman_numeral_map = (('M', 1000), ('IV', 4), ('I', 1)) -roman_numeral_pattern = re.compile(""" +roman_numeral_pattern = re.compile(''' ^ # beginning of string M{0,3} # thousands - 0 to 3 M's (CM|CD|D?C{0,3}) # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's), @@ -34,16 +34,16 @@ roman_numeral_pattern = re.compile(""" (IX|IV|V?I{0,3}) # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's), # or 5-8 (V, followed by 0 to 3 I's) $ # end of string - """, re.VERBOSE) + ''', re.VERBOSE) def to_roman(n): - """convert integer to Roman numeral""" + '''convert integer to Roman numeral''' if not (0 < n < 4000): - raise OutOfRangeError("number out of range (must be 0..3999)") + raise OutOfRangeError('number out of range (must be 0..3999)') if not isinstance(n, int): - raise NotIntegerError("non-integers can not be converted") + raise NotIntegerError('non-integers can not be converted') - result = "" + result = '' for numeral, integer in roman_numeral_map: while n >= integer: result += numeral @@ -51,9 +51,9 @@ def to_roman(n): return result def from_roman(s): - """convert Roman numeral to integer""" + '''convert Roman numeral to integer''' if not roman_numeral_pattern.search(s): - raise InvalidRomanNumeralError("Invalid Roman numeral: {0}".format(s)) + raise InvalidRomanNumeralError('Invalid Roman numeral: {0}'.format(s)) result = 0 index = 0 diff --git a/examples/roman7.py b/examples/roman7.py index 8730a5b..2346d09 100644 --- a/examples/roman7.py +++ b/examples/roman7.py @@ -1,9 +1,9 @@ -"""Convert to and from Roman numerals +'''Convert to and from Roman numerals -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' import re class OutOfRangeError(ValueError): pass @@ -24,7 +24,7 @@ roman_numeral_map = (('M', 1000), ('IV', 4), ('I', 1)) -roman_numeral_pattern = re.compile(""" +roman_numeral_pattern = re.compile(''' ^ # beginning of string M{0,3} # thousands - 0 to 3 M's (CM|CD|D?C{0,3}) # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's), @@ -34,16 +34,16 @@ roman_numeral_pattern = re.compile(""" (IX|IV|V?I{0,3}) # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's), # or 5-8 (V, followed by 0 to 3 I's) $ # end of string - """, re.VERBOSE) + ''', re.VERBOSE) def to_roman(n): - """convert integer to Roman numeral""" + '''convert integer to Roman numeral''' if not (0 < n < 4000): - raise OutOfRangeError("number out of range (must be 0..3999)") + raise OutOfRangeError('number out of range (must be 0..3999)') if not isinstance(n, int): - raise NotIntegerError("non-integers can not be converted") + raise NotIntegerError('non-integers can not be converted') - result = "" + result = '' for numeral, integer in roman_numeral_map: while n >= integer: result += numeral @@ -51,11 +51,11 @@ def to_roman(n): return result def from_roman(s): - """convert Roman numeral to integer""" + '''convert Roman numeral to integer''' if not isinstance(s, str): - raise InvalidRomanNumeralError("Input must be a string") + raise InvalidRomanNumeralError('Input must be a string') if not roman_numeral_pattern.search(s): - raise InvalidRomanNumeralError("Invalid Roman numeral: {0}".format(s)) + raise InvalidRomanNumeralError('Invalid Roman numeral: {0}'.format(s)) result = 0 index = 0 diff --git a/examples/roman8.py b/examples/roman8.py index 742fe7f..2fbd825 100644 --- a/examples/roman8.py +++ b/examples/roman8.py @@ -1,9 +1,9 @@ -"""Convert to and from Roman numerals +'''Convert to and from Roman numerals -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' import re class OutOfRangeError(ValueError): pass @@ -24,7 +24,7 @@ roman_numeral_map = (('M', 1000), ('IV', 4), ('I', 1)) -roman_numeral_pattern = re.compile(""" +roman_numeral_pattern = re.compile(''' ^ # beginning of string M{0,3} # thousands - 0 to 3 M's (CM|CD|D?C{0,3}) # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's), @@ -34,16 +34,16 @@ roman_numeral_pattern = re.compile(""" (IX|IV|V?I{0,3}) # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's), # or 5-8 (V, followed by 0 to 3 I's) $ # end of string - """, re.VERBOSE) + ''', re.VERBOSE) def to_roman(n): - """convert integer to Roman numeral""" + '''convert integer to Roman numeral''' if not (0 < n < 4000): - raise OutOfRangeError("number out of range (must be 0..3999)") + raise OutOfRangeError('number out of range (must be 0..3999)') if not isinstance(n, int): - raise NotIntegerError("non-integers can not be converted") + raise NotIntegerError('non-integers can not be converted') - result = "" + result = '' for numeral, integer in roman_numeral_map: while n >= integer: result += numeral @@ -51,13 +51,13 @@ def to_roman(n): return result def from_roman(s): - """convert Roman numeral to integer""" + '''convert Roman numeral to integer''' if not isinstance(s, str): - raise InvalidRomanNumeralError("Input must be a string") + raise InvalidRomanNumeralError('Input must be a string') if not s: - raise InvalidRomanNumeralError("Input can not be blank") + raise InvalidRomanNumeralError('Input can not be blank') if not roman_numeral_pattern.search(s): - raise InvalidRomanNumeralError("Invalid Roman numeral: {0}".format(s)) + raise InvalidRomanNumeralError('Invalid Roman numeral: {0}'.format(s)) result = 0 index = 0 diff --git a/examples/roman9.py b/examples/roman9.py index 85e09c4..64134b5 100644 --- a/examples/roman9.py +++ b/examples/roman9.py @@ -1,9 +1,9 @@ -"""Convert to and from Roman numerals +'''Convert to and from Roman numerals -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' import re class OutOfRangeError(ValueError): pass @@ -24,7 +24,7 @@ roman_numeral_map = (('M', 1000), ('IV', 4), ('I', 1)) -roman_numeral_pattern = re.compile(""" +roman_numeral_pattern = re.compile(''' ^ # beginning of string M{0,4} # thousands - 0 to 4 M's (CM|CD|D?C{0,3}) # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's), @@ -34,16 +34,16 @@ roman_numeral_pattern = re.compile(""" (IX|IV|V?I{0,3}) # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's), # or 5-8 (V, followed by 0 to 3 I's) $ # end of string - """, re.VERBOSE) + ''', re.VERBOSE) def to_roman(n): - """convert integer to Roman numeral""" + '''convert integer to Roman numeral''' if not (0 < n < 5000): - raise OutOfRangeError("number out of range (must be 0..4999)") + raise OutOfRangeError('number out of range (must be 0..4999)') if not isinstance(n, int): - raise NotIntegerError("non-integers can not be converted") + raise NotIntegerError('non-integers can not be converted') - result = "" + result = '' for numeral, integer in roman_numeral_map: while n >= integer: result += numeral @@ -51,13 +51,13 @@ def to_roman(n): return result def from_roman(s): - """convert Roman numeral to integer""" + '''convert Roman numeral to integer''' if not isinstance(s, str): - raise InvalidRomanNumeralError("Input must be a string") + raise InvalidRomanNumeralError('Input must be a string') if not s: - raise InvalidRomanNumeralError("Input can not be blank") + raise InvalidRomanNumeralError('Input can not be blank') if not roman_numeral_pattern.search(s): - raise InvalidRomanNumeralError("Invalid Roman numeral: {0}".format(s)) + raise InvalidRomanNumeralError('Invalid Roman numeral: {0}'.format(s)) result = 0 index = 0 diff --git a/examples/romantest1.py b/examples/romantest1.py index f6c234a..6a11c6f 100644 --- a/examples/romantest1.py +++ b/examples/romantest1.py @@ -1,9 +1,9 @@ -"""Unit test for roman1.py +'''Unit test for roman1.py -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' import roman1 import unittest @@ -67,12 +67,12 @@ class KnownValues(unittest.TestCase): (3999, 'MMMCMXCIX')) def test_to_roman_known_values(self): - """to_roman should give known result with known input""" + '''to_roman should give known result with known input''' for integer, numeral in self.known_values: result = roman1.to_roman(integer) self.assertEqual(numeral, result) -if __name__ == "__main__": +if __name__ == '__main__': unittest.main() # Copyright (c) 2009, Mark Pilgrim, All rights reserved. diff --git a/examples/romantest10.py b/examples/romantest10.py index dc48dd1..3f982e4 100644 --- a/examples/romantest10.py +++ b/examples/romantest10.py @@ -1,9 +1,9 @@ -"""Unit test for roman1.py +'''Unit test for roman1.py -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' import roman10 import unittest @@ -71,68 +71,68 @@ class KnownValues(unittest.TestCase): (4999, 'MMMMCMXCIX')) def test_to_roman_known_values(self): - """to_roman should give known result with known input""" + '''to_roman should give known result with known input''' for integer, numeral in self.known_values: result = roman10.to_roman(integer) self.assertEqual(numeral, result) def test_from_roman_known_values(self): - """from_roman should give known result with known input""" + '''from_roman should give known result with known input''' for integer, numeral in self.known_values: result = roman10.from_roman(numeral) self.assertEqual(integer, result) class ToRomanBadInput(unittest.TestCase): def test_too_large(self): - """to_roman should fail with large input""" + '''to_roman should fail with large input''' self.assertRaises(roman10.OutOfRangeError, roman10.to_roman, 5000) def test_zero(self): - """to_roman should fail with 0 input""" + '''to_roman should fail with 0 input''' self.assertRaises(roman10.OutOfRangeError, roman10.to_roman, 0) def test_negative(self): - """to_roman should fail with negative input""" + '''to_roman should fail with negative input''' self.assertRaises(roman10.OutOfRangeError, roman10.to_roman, -1) def test_non_integer(self): - """to_roman should fail with non-integer input""" + '''to_roman should fail with non-integer input''' self.assertRaises(roman10.NotIntegerError, roman10.to_roman, 0.5) class FromRomanBadInput(unittest.TestCase): def test_too_many_repeated_numerals(self): - """from_roman should fail with too many repeated numerals""" + '''from_roman should fail with too many repeated numerals''' for s in ('MMMMM', 'DD', 'CCCC', 'LL', 'XXXX', 'VV', 'IIII'): self.assertRaises(roman10.InvalidRomanNumeralError, roman10.from_roman, s) def test_repeated_pairs(self): - """from_roman should fail with repeated pairs of numerals""" + '''from_roman should fail with repeated pairs of numerals''' for s in ('CMCM', 'CDCD', 'XCXC', 'XLXL', 'IXIX', 'IVIV'): self.assertRaises(roman10.InvalidRomanNumeralError, roman10.from_roman, s) def test_malformed_antecedents(self): - """from_roman should fail with malformed antecedents""" + '''from_roman should fail with malformed antecedents''' for s in ('IIMXCC', 'VX', 'DCM', 'CMM', 'IXIV', 'MCMC', 'XCX', 'IVI', 'LM', 'LD', 'LC'): self.assertRaises(roman10.InvalidRomanNumeralError, roman10.from_roman, s) def test_blank(self): - """from_roman should fail with blank string""" - self.assertRaises(roman10.InvalidRomanNumeralError, roman10.from_roman, "") + '''from_roman should fail with blank string''' + self.assertRaises(roman10.InvalidRomanNumeralError, roman10.from_roman, '') def test_non_string(self): - """from_roman should fail with non-string input""" + '''from_roman should fail with non-string input''' self.assertRaises(roman10.InvalidRomanNumeralError, roman10.from_roman, 1) class RoundtripCheck(unittest.TestCase): def test_roundtrip(self): - """from_roman(to_roman(n))==n for all n""" + '''from_roman(to_roman(n))==n for all n''' for integer in range(1, 5000): numeral = roman10.to_roman(integer) result = roman10.from_roman(numeral) self.assertEqual(integer, result) -if __name__ == "__main__": +if __name__ == '__main__': unittest.main() # Copyright (c) 2009, Mark Pilgrim, All rights reserved. diff --git a/examples/romantest2.py b/examples/romantest2.py index e3a1388..9437972 100644 --- a/examples/romantest2.py +++ b/examples/romantest2.py @@ -1,9 +1,9 @@ -"""Unit test for roman1.py +'''Unit test for roman1.py -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' import roman2 import unittest @@ -67,17 +67,17 @@ class KnownValues(unittest.TestCase): (3999, 'MMMCMXCIX')) def test_to_roman_known_values(self): - """to_roman should give known result with known input""" + '''to_roman should give known result with known input''' for integer, numeral in self.known_values: result = roman2.to_roman(integer) self.assertEqual(numeral, result) class ToRomanBadInput(unittest.TestCase): def test_too_large(self): - """to_roman should fail with large input""" + '''to_roman should fail with large input''' self.assertRaises(roman2.OutOfRangeError, roman2.to_roman, 4000) -if __name__ == "__main__": +if __name__ == '__main__': unittest.main() # Copyright (c) 2009, Mark Pilgrim, All rights reserved. diff --git a/examples/romantest3.py b/examples/romantest3.py index d796ac9..cad12c6 100644 --- a/examples/romantest3.py +++ b/examples/romantest3.py @@ -1,9 +1,9 @@ -"""Unit test for roman1.py +'''Unit test for roman1.py -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' import roman3 import unittest @@ -67,25 +67,25 @@ class KnownValues(unittest.TestCase): (3999, 'MMMCMXCIX')) def test_to_roman_known_values(self): - """to_roman should give known result with known input""" + '''to_roman should give known result with known input''' for integer, numeral in self.known_values: result = roman3.to_roman(integer) self.assertEqual(numeral, result) class ToRomanBadInput(unittest.TestCase): def test_too_large(self): - """to_roman should fail with large input""" + '''to_roman should fail with large input''' self.assertRaises(roman3.OutOfRangeError, roman3.to_roman, 4000) def test_zero(self): - """to_roman should fail with 0 input""" + '''to_roman should fail with 0 input''' self.assertRaises(roman3.OutOfRangeError, roman3.to_roman, 0) def test_negative(self): - """to_roman should fail with negative input""" + '''to_roman should fail with negative input''' self.assertRaises(roman3.OutOfRangeError, roman3.to_roman, -1) -if __name__ == "__main__": +if __name__ == '__main__': unittest.main() # Copyright (c) 2009, Mark Pilgrim, All rights reserved. diff --git a/examples/romantest4.py b/examples/romantest4.py index 59d9a90..fa2efdb 100644 --- a/examples/romantest4.py +++ b/examples/romantest4.py @@ -1,9 +1,9 @@ -"""Unit test for roman1.py +'''Unit test for roman1.py -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' import roman4 import unittest @@ -67,29 +67,29 @@ class KnownValues(unittest.TestCase): (3999, 'MMMCMXCIX')) def test_to_roman_known_values(self): - """to_roman should give known result with known input""" + '''to_roman should give known result with known input''' for integer, numeral in self.known_values: result = roman4.to_roman(integer) self.assertEqual(numeral, result) class ToRomanBadInput(unittest.TestCase): def test_too_large(self): - """to_roman should fail with large input""" + '''to_roman should fail with large input''' self.assertRaises(roman4.OutOfRangeError, roman4.to_roman, 4000) def test_zero(self): - """to_roman should fail with 0 input""" + '''to_roman should fail with 0 input''' self.assertRaises(roman4.OutOfRangeError, roman4.to_roman, 0) def test_negative(self): - """to_roman should fail with negative input""" + '''to_roman should fail with negative input''' self.assertRaises(roman4.OutOfRangeError, roman4.to_roman, -1) def test_non_integer(self): - """to_roman should fail with non-integer input""" + '''to_roman should fail with non-integer input''' self.assertRaises(roman4.NotIntegerError, roman4.to_roman, 0.5) -if __name__ == "__main__": +if __name__ == '__main__': unittest.main() # Copyright (c) 2009, Mark Pilgrim, All rights reserved. diff --git a/examples/romantest5.py b/examples/romantest5.py index 2a6116c..80438e8 100644 --- a/examples/romantest5.py +++ b/examples/romantest5.py @@ -1,9 +1,9 @@ -"""Unit test for roman1.py +'''Unit test for roman1.py -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' import roman5 import unittest @@ -67,43 +67,43 @@ class KnownValues(unittest.TestCase): (3999, 'MMMCMXCIX')) def test_to_roman_known_values(self): - """to_roman should give known result with known input""" + '''to_roman should give known result with known input''' for integer, numeral in self.known_values: result = roman5.to_roman(integer) self.assertEqual(numeral, result) def test_from_roman_known_values(self): - """from_roman should give known result with known input""" + '''from_roman should give known result with known input''' for integer, numeral in self.known_values: result = roman5.from_roman(numeral) self.assertEqual(integer, result) class ToRomanBadInput(unittest.TestCase): def test_too_large(self): - """to_roman should fail with large input""" + '''to_roman should fail with large input''' self.assertRaises(roman5.OutOfRangeError, roman5.to_roman, 4000) def test_zero(self): - """to_roman should fail with 0 input""" + '''to_roman should fail with 0 input''' self.assertRaises(roman5.OutOfRangeError, roman5.to_roman, 0) def test_negative(self): - """to_roman should fail with negative input""" + '''to_roman should fail with negative input''' self.assertRaises(roman5.OutOfRangeError, roman5.to_roman, -1) def test_non_integer(self): - """to_roman should fail with non-integer input""" + '''to_roman should fail with non-integer input''' self.assertRaises(roman5.NotIntegerError, roman5.to_roman, 0.5) class RoundtripCheck(unittest.TestCase): def test_roundtrip(self): - """from_roman(to_roman(n))==n for all n""" + '''from_roman(to_roman(n))==n for all n''' for integer in range(1, 4000): numeral = roman5.to_roman(integer) result = roman5.from_roman(numeral) self.assertEqual(integer, result) -if __name__ == "__main__": +if __name__ == '__main__': unittest.main() # Copyright (c) 2009, Mark Pilgrim, All rights reserved. diff --git a/examples/romantest6.py b/examples/romantest6.py index ead625c..201f6e9 100644 --- a/examples/romantest6.py +++ b/examples/romantest6.py @@ -1,9 +1,9 @@ -"""Unit test for roman1.py +'''Unit test for roman1.py -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' import roman6 import unittest @@ -67,60 +67,60 @@ class KnownValues(unittest.TestCase): (3999, 'MMMCMXCIX')) def test_to_roman_known_values(self): - """to_roman should give known result with known input""" + '''to_roman should give known result with known input''' for integer, numeral in self.known_values: result = roman6.to_roman(integer) self.assertEqual(numeral, result) def test_from_roman_known_values(self): - """from_roman should give known result with known input""" + '''from_roman should give known result with known input''' for integer, numeral in self.known_values: result = roman6.from_roman(numeral) self.assertEqual(integer, result) class ToRomanBadInput(unittest.TestCase): def test_too_large(self): - """to_roman should fail with large input""" + '''to_roman should fail with large input''' self.assertRaises(roman6.OutOfRangeError, roman6.to_roman, 4000) def test_zero(self): - """to_roman should fail with 0 input""" + '''to_roman should fail with 0 input''' self.assertRaises(roman6.OutOfRangeError, roman6.to_roman, 0) def test_negative(self): - """to_roman should fail with negative input""" + '''to_roman should fail with negative input''' self.assertRaises(roman6.OutOfRangeError, roman6.to_roman, -1) def test_non_integer(self): - """to_roman should fail with non-integer input""" + '''to_roman should fail with non-integer input''' self.assertRaises(roman6.NotIntegerError, roman6.to_roman, 0.5) class FromRomanBadInput(unittest.TestCase): def test_too_many_repeated_numerals(self): - """from_roman should fail with too many repeated numerals""" + '''from_roman should fail with too many repeated numerals''' for s in ('MMMM', 'DD', 'CCCC', 'LL', 'XXXX', 'VV', 'IIII'): self.assertRaises(roman6.InvalidRomanNumeralError, roman6.from_roman, s) def test_repeated_pairs(self): - """from_roman should fail with repeated pairs of numerals""" + '''from_roman should fail with repeated pairs of numerals''' for s in ('CMCM', 'CDCD', 'XCXC', 'XLXL', 'IXIX', 'IVIV'): self.assertRaises(roman6.InvalidRomanNumeralError, roman6.from_roman, s) def test_malformed_antecedents(self): - """from_roman should fail with malformed antecedents""" + '''from_roman should fail with malformed antecedents''' for s in ('IIMXCC', 'VX', 'DCM', 'CMM', 'IXIV', 'MCMC', 'XCX', 'IVI', 'LM', 'LD', 'LC'): self.assertRaises(roman6.InvalidRomanNumeralError, roman6.from_roman, s) class RoundtripCheck(unittest.TestCase): def test_roundtrip(self): - """from_roman(to_roman(n))==n for all n""" + '''from_roman(to_roman(n))==n for all n''' for integer in range(1, 4000): numeral = roman6.to_roman(integer) result = roman6.from_roman(numeral) self.assertEqual(integer, result) -if __name__ == "__main__": +if __name__ == '__main__': unittest.main() # Copyright (c) 2009, Mark Pilgrim, All rights reserved. diff --git a/examples/romantest7.py b/examples/romantest7.py index ecf74dc..3b0821a 100644 --- a/examples/romantest7.py +++ b/examples/romantest7.py @@ -1,9 +1,9 @@ -"""Unit test for roman1.py +'''Unit test for roman1.py -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' import roman7 import unittest @@ -67,64 +67,64 @@ class KnownValues(unittest.TestCase): (3999, 'MMMCMXCIX')) def test_to_roman_known_values(self): - """to_roman should give known result with known input""" + '''to_roman should give known result with known input''' for integer, numeral in self.known_values: result = roman7.to_roman(integer) self.assertEqual(numeral, result) def test_from_roman_known_values(self): - """from_roman should give known result with known input""" + '''from_roman should give known result with known input''' for integer, numeral in self.known_values: result = roman7.from_roman(numeral) self.assertEqual(integer, result) class ToRomanBadInput(unittest.TestCase): def test_too_large(self): - """to_roman should fail with large input""" + '''to_roman should fail with large input''' self.assertRaises(roman7.OutOfRangeError, roman7.to_roman, 4000) def test_zero(self): - """to_roman should fail with 0 input""" + '''to_roman should fail with 0 input''' self.assertRaises(roman7.OutOfRangeError, roman7.to_roman, 0) def test_negative(self): - """to_roman should fail with negative input""" + '''to_roman should fail with negative input''' self.assertRaises(roman7.OutOfRangeError, roman7.to_roman, -1) def test_non_integer(self): - """to_roman should fail with non-integer input""" + '''to_roman should fail with non-integer input''' self.assertRaises(roman7.NotIntegerError, roman7.to_roman, 0.5) class FromRomanBadInput(unittest.TestCase): def test_too_many_repeated_numerals(self): - """from_roman should fail with too many repeated numerals""" + '''from_roman should fail with too many repeated numerals''' for s in ('MMMM', 'DD', 'CCCC', 'LL', 'XXXX', 'VV', 'IIII'): self.assertRaises(roman7.InvalidRomanNumeralError, roman7.from_roman, s) def test_repeated_pairs(self): - """from_roman should fail with repeated pairs of numerals""" + '''from_roman should fail with repeated pairs of numerals''' for s in ('CMCM', 'CDCD', 'XCXC', 'XLXL', 'IXIX', 'IVIV'): self.assertRaises(roman7.InvalidRomanNumeralError, roman7.from_roman, s) def test_malformed_antecedents(self): - """from_roman should fail with malformed antecedents""" + '''from_roman should fail with malformed antecedents''' for s in ('IIMXCC', 'VX', 'DCM', 'CMM', 'IXIV', 'MCMC', 'XCX', 'IVI', 'LM', 'LD', 'LC'): self.assertRaises(roman7.InvalidRomanNumeralError, roman7.from_roman, s) def test_non_string(self): - """from_roman should fail with non-string input""" + '''from_roman should fail with non-string input''' self.assertRaises(roman7.InvalidRomanNumeralError, roman7.from_roman, 1) class RoundtripCheck(unittest.TestCase): def test_roundtrip(self): - """from_roman(to_roman(n))==n for all n""" + '''from_roman(to_roman(n))==n for all n''' for integer in range(1, 4000): numeral = roman7.to_roman(integer) result = roman7.from_roman(numeral) self.assertEqual(integer, result) -if __name__ == "__main__": +if __name__ == '__main__': unittest.main() # Copyright (c) 2009, Mark Pilgrim, All rights reserved. diff --git a/examples/romantest8.py b/examples/romantest8.py index 7cfb46b..bc69059 100644 --- a/examples/romantest8.py +++ b/examples/romantest8.py @@ -1,9 +1,9 @@ -"""Unit test for roman1.py +'''Unit test for roman1.py -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' import roman8 import unittest @@ -67,68 +67,68 @@ class KnownValues(unittest.TestCase): (3999, 'MMMCMXCIX')) def test_to_roman_known_values(self): - """to_roman should give known result with known input""" + '''to_roman should give known result with known input''' for integer, numeral in self.known_values: result = roman8.to_roman(integer) self.assertEqual(numeral, result) def test_from_roman_known_values(self): - """from_roman should give known result with known input""" + '''from_roman should give known result with known input''' for integer, numeral in self.known_values: result = roman8.from_roman(numeral) self.assertEqual(integer, result) class ToRomanBadInput(unittest.TestCase): def test_too_large(self): - """to_roman should fail with large input""" + '''to_roman should fail with large input''' self.assertRaises(roman8.OutOfRangeError, roman8.to_roman, 4000) def test_zero(self): - """to_roman should fail with 0 input""" + '''to_roman should fail with 0 input''' self.assertRaises(roman8.OutOfRangeError, roman8.to_roman, 0) def test_negative(self): - """to_roman should fail with negative input""" + '''to_roman should fail with negative input''' self.assertRaises(roman8.OutOfRangeError, roman8.to_roman, -1) def test_non_integer(self): - """to_roman should fail with non-integer input""" + '''to_roman should fail with non-integer input''' self.assertRaises(roman8.NotIntegerError, roman8.to_roman, 0.5) class FromRomanBadInput(unittest.TestCase): def test_too_many_repeated_numerals(self): - """from_roman should fail with too many repeated numerals""" + '''from_roman should fail with too many repeated numerals''' for s in ('MMMM', 'DD', 'CCCC', 'LL', 'XXXX', 'VV', 'IIII'): self.assertRaises(roman8.InvalidRomanNumeralError, roman8.from_roman, s) def test_repeated_pairs(self): - """from_roman should fail with repeated pairs of numerals""" + '''from_roman should fail with repeated pairs of numerals''' for s in ('CMCM', 'CDCD', 'XCXC', 'XLXL', 'IXIX', 'IVIV'): self.assertRaises(roman8.InvalidRomanNumeralError, roman8.from_roman, s) def test_malformed_antecedents(self): - """from_roman should fail with malformed antecedents""" + '''from_roman should fail with malformed antecedents''' for s in ('IIMXCC', 'VX', 'DCM', 'CMM', 'IXIV', 'MCMC', 'XCX', 'IVI', 'LM', 'LD', 'LC'): self.assertRaises(roman8.InvalidRomanNumeralError, roman8.from_roman, s) def test_blank(self): - """from_roman should fail with blank string""" - self.assertRaises(roman8.InvalidRomanNumeralError, roman8.from_roman, "") + '''from_roman should fail with blank string''' + self.assertRaises(roman8.InvalidRomanNumeralError, roman8.from_roman, '') def test_non_string(self): - """from_roman should fail with non-string input""" + '''from_roman should fail with non-string input''' self.assertRaises(roman8.InvalidRomanNumeralError, roman8.from_roman, 1) class RoundtripCheck(unittest.TestCase): def test_roundtrip(self): - """from_roman(to_roman(n))==n for all n""" + '''from_roman(to_roman(n))==n for all n''' for integer in range(1, 4000): numeral = roman8.to_roman(integer) result = roman8.from_roman(numeral) self.assertEqual(integer, result) -if __name__ == "__main__": +if __name__ == '__main__': unittest.main() # Copyright (c) 2009, Mark Pilgrim, All rights reserved. diff --git a/examples/romantest9.py b/examples/romantest9.py index 2ce118b..4225b0b 100644 --- a/examples/romantest9.py +++ b/examples/romantest9.py @@ -1,9 +1,9 @@ -"""Unit test for roman1.py +'''Unit test for roman1.py -This program is part of "Dive Into Python 3", a free Python book for +This program is part of 'Dive Into Python 3', a free Python book for experienced programmers. Visit http://diveintopython3.org/ for the latest version. -""" +''' import roman9 import unittest @@ -71,68 +71,68 @@ class KnownValues(unittest.TestCase): (4999, 'MMMMCMXCIX')) def test_to_roman_known_values(self): - """to_roman should give known result with known input""" + '''to_roman should give known result with known input''' for integer, numeral in self.known_values: result = roman9.to_roman(integer) self.assertEqual(numeral, result) def test_from_roman_known_values(self): - """from_roman should give known result with known input""" + '''from_roman should give known result with known input''' for integer, numeral in self.known_values: result = roman9.from_roman(numeral) self.assertEqual(integer, result) class ToRomanBadInput(unittest.TestCase): def test_too_large(self): - """to_roman should fail with large input""" + '''to_roman should fail with large input''' self.assertRaises(roman9.OutOfRangeError, roman9.to_roman, 5000) def test_zero(self): - """to_roman should fail with 0 input""" + '''to_roman should fail with 0 input''' self.assertRaises(roman9.OutOfRangeError, roman9.to_roman, 0) def test_negative(self): - """to_roman should fail with negative input""" + '''to_roman should fail with negative input''' self.assertRaises(roman9.OutOfRangeError, roman9.to_roman, -1) def test_non_integer(self): - """to_roman should fail with non-integer input""" + '''to_roman should fail with non-integer input''' self.assertRaises(roman9.NotIntegerError, roman9.to_roman, 0.5) class FromRomanBadInput(unittest.TestCase): def test_too_many_repeated_numerals(self): - """from_roman should fail with too many repeated numerals""" + '''from_roman should fail with too many repeated numerals''' for s in ('MMMMM', 'DD', 'CCCC', 'LL', 'XXXX', 'VV', 'IIII'): self.assertRaises(roman9.InvalidRomanNumeralError, roman9.from_roman, s) def test_repeated_pairs(self): - """from_roman should fail with repeated pairs of numerals""" + '''from_roman should fail with repeated pairs of numerals''' for s in ('CMCM', 'CDCD', 'XCXC', 'XLXL', 'IXIX', 'IVIV'): self.assertRaises(roman9.InvalidRomanNumeralError, roman9.from_roman, s) def test_malformed_antecedents(self): - """from_roman should fail with malformed antecedents""" + '''from_roman should fail with malformed antecedents''' for s in ('IIMXCC', 'VX', 'DCM', 'CMM', 'IXIV', 'MCMC', 'XCX', 'IVI', 'LM', 'LD', 'LC'): self.assertRaises(roman9.InvalidRomanNumeralError, roman9.from_roman, s) def test_blank(self): - """from_roman should fail with blank string""" - self.assertRaises(roman9.InvalidRomanNumeralError, roman9.from_roman, "") + '''from_roman should fail with blank string''' + self.assertRaises(roman9.InvalidRomanNumeralError, roman9.from_roman, '') def test_non_string(self): - """from_roman should fail with non-string input""" + '''from_roman should fail with non-string input''' self.assertRaises(roman9.InvalidRomanNumeralError, roman9.from_roman, 1) class RoundtripCheck(unittest.TestCase): def test_roundtrip(self): - """from_roman(to_roman(n))==n for all n""" + '''from_roman(to_roman(n))==n for all n''' for integer in range(1, 5000): numeral = roman9.to_roman(integer) result = roman9.from_roman(numeral) self.assertEqual(integer, result) -if __name__ == "__main__": +if __name__ == '__main__': unittest.main() # Copyright (c) 2009, Mark Pilgrim, All rights reserved. diff --git a/files.html b/files.html index 1fb05cb..c54b07d 100644 --- a/files.html +++ b/files.html @@ -26,7 +26,7 @@ body{counter-reset:h1 12} OK, so a string is a sequence of Unicode characters. But a file on disk is not a sequence of Unicode characters; a file on disk is a sequence of bytes. So if you read a “text file” from disk, how does Python convert that sequence of bytes into a sequence of characters? The answer is that it decodes the bytes according to a specific character encoding algorithm, and returns a sequence of Unicode characters, otherwise known as a string. --> -

    © 2001–9 Mark Pilgrim diff --git a/generators.html b/generators.html index abdbfcf..22d02b3 100644 --- a/generators.html +++ b/generators.html @@ -406,7 +406,7 @@ def plural(noun):

  • PEP 255: Simple Generators -

    © 2001–9 Mark Pilgrim diff --git a/http-web-services.html b/http-web-services.html index 57ea528..74a7119 100644 --- a/http-web-services.html +++ b/http-web-services.html @@ -30,7 +30,7 @@ mark{display:inline}

  • Google Data APIs allow you to interact with a wide variety of Google services, including Blogger and YouTube.
  • Flickr Services allow you to upload and download photos from Flickr.
  • Twitter API allows you to publish status updates on Twitter. -
  • …and many more +
  • …and many more

    Python 3 comes with two different libraries for interacting with HTTP web services: @@ -153,7 +153,7 @@ Cache-Control: max-age=31536000, public

  • Compression

    -

    When you talk about HTTP web services, you’re almost always talking about moving text-based data back and forth over the wire. Maybe it’s XML, maybe it’s JSON, maybe it’s just plain text. Regardless of the format, text compresses well. The example feed in the XML chapter is 3070 bytes uncompressed, but would be 941 bytes after gzip compression. That’s just 30% of the original size! +

    When you talk about HTTP web services, you’re almost always talking about moving text-based data back and forth over the wire. Maybe it’s XML, maybe it’s JSON, maybe it’s just plain text. Regardless of the format, text compresses well. The example feed in the XML chapter is 3070 bytes uncompressed, but would be 941 bytes after gzip compression. That’s just 30% of the original size!

    HTTP supports several compression algorithms. The two most common types are gzip and deflate. When you request a resource over HTTP, you can ask the server to send it in compressed format. You include an Accept-encoding header in your request that lists which compression algorithms you support. If the server supports any of the same algorithms, it will send you back compressed data (with a Content-encoding header that tells you which algorithm it used). Then it’s up to you to decompress the data. @@ -190,13 +190,13 @@ Cache-Control: max-age=31536000, public >>> import urllib.request >>> data = urllib.request.urlopen('http://diveintopython3.org/examples/feed.xml').read() >>> print(data) -<?xml version="1.0" encoding="utf-8"?> -<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"> +<?xml version='1.0' encoding='utf-8'?> +<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'> <title>dive into mark</title> <subtitle>currently between addictions</subtitle> <id>tag:diveintomark.org,2001-07-29:/</id> <updated>2009-03-27T21:56:07Z</updated> - <link rel="alternate" type="text/html" href="http://diveintomark.org/"/> + <link rel='alternate' type='text/html' href='http://diveintomark.org/'/> …

      @@ -320,7 +320,7 @@ Content-Type: application/xml >>> response.status 200 >>> content[:52] -b'<?xml version="1.0" encoding="utf-8"?>\r\n<feed xmlns=' +b"<?xml version='1.0' encoding='utf-8'?>\r\n<feed xmlns=" >>> len(content) 3070
        @@ -337,7 +337,7 @@ Content-Type: application/xml >>> response2.status 200 >>> content2[:52] -b'<?xml version="1.0" encoding="utf-8"?>\r\n<feed xmlns=' +b"<?xml version='1.0' encoding='utf-8'?>\r\n<feed xmlns=" >>> len(content2) 3070
          @@ -551,9 +551,9 @@ reply: 'HTTP/1.1 301 Moved Permanently' >>> import httplib2 >>> from urllib.parse import urlencode >>> h = httplib2.Http('.cache') ->>> data = {"status": "Test update from Python 3"} ->>> h.add_credentials("diveintomark", "MY_SECRET_PASSWORD") ->>> resp, content = h.request("http://twitter.com/statuses/update.xml", "POST", urlencode(data)) +>>> data = {'status': 'Test update from Python 3'} +>>> h.add_credentials('diveintomark', 'MY_SECRET_PASSWORD') +>>> resp, content = h.request('http://twitter.com/statuses/update.xml', 'POST', urlencode(data)) >>> resp.status 200 >>> from xml.etree import ElementTree as etree @@ -608,9 +608,9 @@ reply: 'HTTP/1.1 301 Moved Permanently'
           # continued from the previous example
          ->>> tree.findtext("id")
          +>>> tree.findtext('id')
           '1973974228'
          ->>> resp, delete_content = h.request("http://twitter.com/statuses/destroy/{0}.xml".format(tree.findtext("id")), "DELETE")
          +>>> resp, delete_content = h.request('http://twitter.com/statuses/destroy/{0}.xml'.format(tree.findtext('id')), 'DELETE')
           >>> resp.status
           200
          @@ -627,6 +627,7 @@ reply: 'HTTP/1.1 301 Moved Permanently'
        1. How to control caching with HTTP headers on Google Doctype +

          © 2001–9 Mark Pilgrim diff --git a/index.html b/index.html index a5baa73..1e8f89c 100644 --- a/index.html +++ b/index.html @@ -5,10 +5,10 @@ @@ -16,7 +16,7 @@ h1:before{content:""}

           
          -

          You are here:   +

          You are here:  

          Dive Into Python 3

          @@ -51,12 +51,12 @@ h1:before{content:""}
        2. Special Method Names
        -

        There is a changelog, a feed, and discussion on Reddit. During development, you can download the book by cloning the Mercurial repository: +

        There is a changelog, a feed, and discussion on Reddit. During development, you can download the book by cloning the Mercurial repository:

        you@localhost:~$ hg clone http://hg.diveintopython3.org/ diveintopython3

        The final version will be downloadable as HTML and PDF. -

        This site is optimized for Lynx just because fuck you.
        I’m told it also looks good in graphical browsers. +

        This site is optimized for Lynx just because fuck you.
        I’m told it also looks good in graphical browsers.

        © 2001–9 Mark Pilgrim diff --git a/iterators-and-generators.html b/iterators-and-generators.html index 3a2c761..e79d2b2 100644 --- a/iterators-and-generators.html +++ b/iterators-and-generators.html @@ -6,7 +6,7 @@ @@ -59,16 +59,15 @@ h1:before{counter-increment:h1;content:""}

        Further Reading

        --> diff --git a/iterators.html b/iterators.html index 230b404..018af2a 100644 --- a/iterators.html +++ b/iterators.html @@ -26,7 +26,7 @@ body{counter-reset:h1 6}

        [download fibonacci2.py]

        class Fib:
        -    """iterator that yields numbers in the Fibonacci sequence"""
        +    '''iterator that yields numbers in the Fibonacci sequence'''
         
             def __init__(self, max):
                 self.max = max
        @@ -67,7 +67,7 @@ class PapayaWhip:  
         
         

        This PapayaWhip class doesn’t define any methods or attributes, but syntactically, there needs to be something in the definition, thus the pass statement. This is a Python reserved word that just means “move along, nothing to see here”. It’s a statement that does nothing, and it’s a good placeholder when you’re stubbing out functions or classes. -

        +

        The pass statement in Python is like a empty set of curly braces ({}) in Java or C.

        @@ -79,7 +79,7 @@ class PapayaWhip:
        
         class Fib:
        -    """iterator that yields numbers in the Fibonacci sequence"""  
        +    '''iterator that yields numbers in the Fibonacci sequence'''  
         
             def __init__(self, max):                                      
          @@ -112,7 +112,7 @@ class Fib:
        1. You can access the instance’s docstring just as with a function or a module. All instances of a class share the same docstring.
        -
        +

        In Python, simply call a class as if it were a function to create a new instance of the class. There is no explicit new operator like C++ or Java.

        @@ -374,7 +374,7 @@ rules = LazyRules()
      1. PEP 255: Simple Generators -

        © 2001–9 Mark Pilgrim diff --git a/native-datatypes.html b/native-datatypes.html index 6a9ab30..11f17fa 100644 --- a/native-datatypes.html +++ b/native-datatypes.html @@ -93,7 +93,7 @@ body{counter-reset:h1 2}

      2. Floating point numbers are accurate to 15 decimal places.
      3. Integers can be arbitrarily large.
      -
      +

      Python 2 had separate types for int and long. The int datatype was limited by sys.maxint, which varied by platform but was usually 232-1. Python 3 has just one integer type, which behaves mostly like the old long type from Python 2. See PEP 237 for details.

      Common Numerical Operations

      @@ -120,7 +120,7 @@ body{counter-reset:h1 2}
    1. The ** operator means “raised to the power of.” 112 is 121.
    2. The % operator gives the remainder after performing integer division. 11 divided by 2 is 5 with a remainder of 1, so the result here is 1.
    -
    +

    In Python 2, the / operator usually meant integer division, but you could make it behave like floating point division by including a special directive in your code. In Python 3, the / operator always means floating point division. See PEP 238 for details.

    Fractions

    @@ -161,9 +161,9 @@ body{counter-reset:h1 2}
     >>> def is_it_true(anything):             
     ...   if anything:
    -...     print("yes, it's true")
    +...     print('yes, it's true')
     ...   else:
    -...     print("no, it's false")
    +...     print('no, it's false')
     ...
     >>> is_it_true(1)                         
     yes, it's true
    @@ -190,10 +190,10 @@ body{counter-reset:h1 2}
     
     

    Lists

    Lists are Python’s workhorse datatype. When I say “list,” you might be thinking “array whose size I have to declare in advance, that can only contain items of the same type, &c.” Don’t think that. Lists are much cooler than that. -

    +

    A list in Python is like an array in Perl 5. In Perl 5, variables that store arrays always start with the @ character; in Python, variables can be named anything, and Python keeps track of the datatype internally.

    -
    +

    A list in Python is much more than an array in Java (although it can be used as one if that’s really all you want out of life). A better analogy would be to the ArrayList class, which can hold arbitrary objects and can expand dynamically as new items are added.

    Creating A List

    @@ -316,9 +316,9 @@ ValueError: list.index(x): x not in list
     >>> def is_it_true(anything):
     ...   if anything:
    -...     print("yes, it's true")
    +...     print('yes, it's true')
     ...   else:
    -...     print("no, it's false")
    +...     print('no, it's false')
     ...
     >>> is_it_true([])             
     no, it's false
    @@ -341,44 +341,44 @@ ValueError: list.index(x): x not in list

    Dictionaries

    One of Python’s most important datatypes is the dictionary, which defines one-to-one relationships between keys and values. -

    +

    A dictionary in Python is like a hash in Perl 5. In Perl 5, variables that store hashes always start with a % character. In Python, variables can be named anything, and Python keeps track of the datatype internally.

    Creating A Dictionary

    Creating a dictionary is easy. The syntax is similar to sets, but instead of values, you have key-value pairs. Once you have a dictionary, you can look up values by their key.

    ->>> a_dict = {"server":"db.diveintopython3.org", "database":"mysql"}  
    +>>> a_dict = {'server':'db.diveintopython3.org', 'database':'mysql'}  
     >>> a_dict
     {'server': 'db.diveintopython3.org', 'database': 'mysql'}
    ->>> a_dict["server"]                                                  
    +>>> a_dict['server']                                                  
     'db.diveintopython3.org'
    ->>> a_dict["database"]                                                
    +>>> a_dict['database']                                                
     'mysql'
    ->>> a_dict["db.diveintopython3.org"]                                  
    +>>> a_dict['db.diveintopython3.org']                                  
     Traceback (most recent call last):
       File "<stdin>", line 1, in <module>
     KeyError: 'db.diveintopython3.org'
    1. First, you create a new dictionary with two items and assign it to the variable a_dict. Each item is a key-value pair, and the whole set of items is enclosed in curly braces. -
    2. 'server' is a key, and its associated value, referenced by a_dict["server"], is 'db.diveintopython3.org'. -
    3. 'database' is a key, and its associated value, referenced by a_dict["database"], is 'mysql'. -
    4. You can get values by key, but you can’t get keys by value. So a_dict["server"] is 'db.diveintopython3.org', but a_dict["db.diveintopython3.org"] raises an exception, because 'db.diveintopython3.org' is not a key. +
    5. 'server' is a key, and its associated value, referenced by a_dict['server'], is 'db.diveintopython3.org'. +
    6. 'database' is a key, and its associated value, referenced by a_dict['database'], is 'mysql'. +
    7. You can get values by key, but you can’t get keys by value. So a_dict['server'] is 'db.diveintopython3.org', but a_dict['db.diveintopython3.org'] raises an exception, because 'db.diveintopython3.org' is not a key.

    Modifying A Dictionary

    Dictionaries do not have any predefined size limit. You can add new key-value pairs to a dictionary at any time, or you can modify the value of an existing key. Continuing from the previous example:

     >>> a_dict
     {'server': 'db.diveintopython3.org', 'database': 'mysql'}
    ->>> a_dict["database"] = "blog"  
    +>>> a_dict['database'] = 'blog'  
     >>> a_dict
     {'server': 'db.diveintopython3.org', 'database': 'blog'}
    ->>> a_dict["user"] = "mark"      
    +>>> a_dict['user'] = 'mark'      
     >>> a_dict                       
     {'server': 'db.diveintopython3.org', 'user': 'mark', 'database': 'blog'}
    ->>> a_dict["user"] = "dora"      
    +>>> a_dict['user'] = 'dora'      
     >>> a_dict
     {'server': 'db.diveintopython3.org', 'user': 'dora', 'database': 'blog'}
    ->>> a_dict["User"] = "mark"      
    +>>> a_dict['User'] = 'mark'      
     >>> a_dict
     {'User': 'mark', 'server': 'db.diveintopython3.org', 'user': 'dora', 'database': 'blog'}
      @@ -417,9 +417,9 @@ KeyError: 'db.diveintopython3.org'
       >>> def is_it_true(anything):
       ...   if anything:
      -...     print("yes, it's true")
      +...     print('yes, it's true')
       ...   else:
      -...     print("no, it's false")
      +...     print('no, it's false')
       ...
       >>> is_it_true({})             
       no, it's false
      @@ -457,9 +457,9 @@ KeyError: 'db.diveintopython3.org'
       >>> def is_it_true(anything):
       ...   if anything:
      -...     print("yes, it's true")
      +...     print('yes, it's true')
       ...   else:
      -...     print("no, it's false")
      +...     print('no, it's false')
       ...
       >>> is_it_true(None)
       no, it's false
      @@ -474,7 +474,7 @@ KeyError: 'db.diveintopython3.org'
    1. PEP 237: Unifying Long Integers and Integers
    2. PEP 238: Changing the Division Operator -

      © 2001–9 Mark Pilgrim diff --git a/porting-code-to-python-3-with-2to3.html b/porting-code-to-python-3-with-2to3.html index c377789..40a8356 100644 --- a/porting-code-to-python-3-with-2to3.html +++ b/porting-code-to-python-3-with-2to3.html @@ -5,9 +5,9 @@ - + -

        
      +
        

      You are here: Home Dive Into Python 3

      Difficulty level: ♦♦♦♦♦

      Porting Code to Python 3 with 2to3

      @@ -67,11 +67,11 @@ td pre{padding:0;border:0} Python 2 Python 3 ① -u"PapayaWhip" -"PapayaWhip" +u'PapayaWhip' +'PapayaWhip' ② -ur"PapayaWhip\foo" -r"PapayaWhip\foo" +ur'PapayaWhip\foo' +r'PapayaWhip\foo'
      1. Unicode string literals are simply converted into string literals, which, in Python 3, are always Unicode. @@ -141,8 +141,8 @@ td pre{padding:0;border:0} Python 2 Python 3 ① -a_dictionary.has_key("PapayaWhip") -"PapayaWhip" in a_dictionary +a_dictionary.has_key('PapayaWhip') +'PapayaWhip' in a_dictionarya_dictionary.has_key(x) or a_dictionary.has_key(y) x in a_dictionary or y in a_dictionary @@ -547,8 +547,8 @@ reduce(a, b, c) Python 2 Python 3 -execfile("a_filename") -exec(compile(open("a_filename").read(), "a_filename", "exec")) +execfile('a_filename') +exec(compile(open('a_filename').read(), 'a_filename', 'exec'))

        The version of 2to3 that shipped with Python 3.0 would not fix the execfile statement automatically. The fix first appeared in the 2to3 script that shipped with Python 3.1. @@ -563,8 +563,8 @@ reduce(a, b, c) `x` repr(x) ② -`"PapayaWhip" + `2`` -repr("PapayaWhip" + repr(2)) +`'PapayaWhip' + `2`` +repr('PapayaWhip' + repr(2))

        1. Remember, x can be anything — a class, a function, a module, a primitive data type, etc. The repr() function works on everything. @@ -626,13 +626,13 @@ except: raise MyException unchanged ② -raise MyException, "error message" -raise MyException("error message") +raise MyException, 'error message' +raise MyException('error message') ③ -raise MyException, "error message", a_traceback -raise MyException("error message").with_traceback(a_traceback) +raise MyException, 'error message', a_traceback +raise MyException('error message').with_traceback(a_traceback) ④ -raise "error message" +raise 'error message' unsupported
            @@ -651,10 +651,10 @@ except: a_generator.throw(MyException) no change ② -a_generator.throw(MyException, "error message") -a_generator.throw(MyException("error message")) +a_generator.throw(MyException, 'error message') +a_generator.throw(MyException('error message')) ③ -a_generator.throw("error message") +a_generator.throw('error message') unsupported
              @@ -701,8 +701,8 @@ except: raw_input() input() ② -raw_input("prompt") -input("prompt") +raw_input('prompt') +input('prompt')input() eval(input()) @@ -766,7 +766,7 @@ except:
            1. If you used to call xreadlines() with no arguments, 2to3 will convert it to just the file object. In Python 3, this will accomplish the same thing: read the file one line at a time and execute the body of the for loop.
            2. If you used to call xreadlines() with an argument (the number of lines to read at a time), keep doing that. It still works in Python 3, and 2to3 will not change it.
            -

            +

            lambda functions that take a tuple instead of multiple parameters

            In Python 2, you could define anonymous lambda functions which took multiple parameters by defining the function as taking a tuple with a specific number of items. In effect, Python 2 would “unpack” the tuple into named arguments, which you could then reference (by name) within the lambda function. In Python 3, you can still pass a tuple to a lambda function, but the Python interpreter will not unpack the tuple into named arguments. Instead, you will need to reference each argument by its positional index. @@ -866,7 +866,7 @@ except:
            Python 3
            callable(anything) -hasattr(anything, "__call__") +hasattr(anything, '__call__')

            zip() global function

            In Python 2, the global zip() function took any number of sequences and returned a list of tuples. The first tuple contained the first item from each sequence; the second tuple contained the second item from each sequence; and so on. In Python 3, zip() returns an iterator instead of a list. @@ -1119,7 +1119,7 @@ do_stuff(a_list)

            FIXME: once the rest of the book is written, this appendix should contain copious links back to any chapter or section that touches on these features. -

            © 2001–9 Mark Pilgrim diff --git a/publish b/publish index d57c0f0..56234a8 100755 --- a/publish +++ b/publish @@ -18,7 +18,7 @@ for f in *.html; do done # build sitemap -ls build/*.html | sed -e "s|build/|http://diveintopython3.org/|g" > build/sitemap.txt +ls build/*.html | sed -e "s|build/|http://diveintopython3.org/|g" -e "s|/index.html|/|g" > build/sitemap.txt echo "adding evil tracking code" diff --git a/refactoring.html b/refactoring.html index 8ad2884..4ea7b6e 100644 --- a/refactoring.html +++ b/refactoring.html @@ -23,7 +23,7 @@ body{counter-reset:h1 10}

            Despite your best efforts to write comprehensive unit tests, bugs happen. What do I mean by “bug”? A bug is a test case you haven’t written yet.

            >>> import roman7
            ->>> roman7.from_roman("") 
            +>>> roman7.from_roman('') 
             0
            1. Remember in the [FIXME-xref] previous section when you kept seeing that an empty string would match the regular expression you were using to check for valid Roman numerals? Well, it turns out that this is still true for the final version of the regular expression. And that’s a bug; you want an empty string to raise an InvalidRomanNumeralError exception just like any other sequence of characters that don’t represent a valid Roman numeral. @@ -36,8 +36,8 @@ body{counter-reset:h1 10} . . def testBlank(self): - """from_roman should fail with blank string""" - self.assertRaises(roman6.InvalidRomanNumeralError, roman6.from_roman, "") + '''from_roman should fail with blank string''' + self.assertRaises(roman6.InvalidRomanNumeralError, roman6.from_roman, '')
              1. Pretty simple stuff here. Call from_roman() with an empty string and make sure it raises an InvalidRomanNumeralError exception. The hard part was finding the bug; now that you know about it, testing for it is the easy part.
              @@ -62,7 +62,7 @@ FAIL: from_roman should fail with blank string ---------------------------------------------------------------------- Traceback (most recent call last): File "romantest8.py", line 117, in test_blank - self.assertRaises(roman8.InvalidRomanNumeralError, roman8.from_roman, "") + self.assertRaises(roman8.InvalidRomanNumeralError, roman8.from_roman, '') AssertionError: InvalidRomanNumeralError not raised by from_roman ---------------------------------------------------------------------- @@ -73,7 +73,7 @@ FAILED (failures=1)

              Now you can fix the bug.

              def from_roman(s):
              -    """convert Roman numeral to integer"""
              +    '''convert Roman numeral to integer'''
                   if not s:  
                       raise InvalidRomanNumeralError, 'Input can not be blank'
                   if not re.search(romanNumeralPattern, s):
              @@ -137,7 +137,7 @@ class KnownValues(unittest.TestCase):
               
               class ToRomanBadInput(unittest.TestCase):
                   def test_too_large(self):
              -        """to_roman should fail with large input"""
              +        '''to_roman should fail with large input'''
                       self.assertRaises(roman8.OutOfRangeError, roman8.to_roman, 5000)  
               
                   .
              @@ -146,7 +146,7 @@ class ToRomanBadInput(unittest.TestCase):
               
               class FromRomanBadInput(unittest.TestCase):
                   def test_too_many_repeated_numerals(self):
              -        """from_roman should fail with too many repeated numerals"""
              +        '''from_roman should fail with too many repeated numerals'''
                       for s in ('MMMMM', 'DD', 'CCCC', 'LL', 'XXXX', 'VV', 'IIII'):     
                           self.assertRaises(roman8.InvalidRomanNumeralError, roman8.from_roman, s)
               
              @@ -156,7 +156,7 @@ class FromRomanBadInput(unittest.TestCase):
               
               class RoundtripCheck(unittest.TestCase):
                   def test_roundtrip(self):
              -        """from_roman(to_roman(n))==n for all n"""
              +        '''from_roman(to_roman(n))==n for all n'''
                       for integer in range(1, 5000):                                    
                           numeral = roman8.to_roman(integer)
                           result = roman8.from_roman(numeral)
              @@ -192,7 +192,7 @@ Traceback (most recent call last):
                 File "romantest9.py", line 82, in test_from_roman_known_values
                   result = roman9.from_roman(numeral)
                 File "C:\home\diveintopython3\examples\roman9.py", line 60, in from_roman
              -    raise InvalidRomanNumeralError("Invalid Roman numeral: {0}".format(s))
              +    raise InvalidRomanNumeralError('Invalid Roman numeral: {0}'.format(s))
               roman9.InvalidRomanNumeralError: Invalid Roman numeral: MMMM
               
               ======================================================================
              @@ -202,7 +202,7 @@ Traceback (most recent call last):
                 File "romantest9.py", line 76, in test_to_roman_known_values
                   result = roman9.to_roman(integer)
                 File "C:\home\diveintopython3\examples\roman9.py", line 42, in to_roman
              -    raise OutOfRangeError("number out of range (must be 0..3999)")
              +    raise OutOfRangeError('number out of range (must be 0..3999)')
               roman9.OutOfRangeError: number out of range (must be 0..3999)
               
               ======================================================================
              @@ -212,7 +212,7 @@ Traceback (most recent call last):
                 File "romantest9.py", line 131, in testSanity
                   numeral = roman9.to_roman(integer)
                 File "C:\home\diveintopython3\examples\roman9.py", line 42, in to_roman
              -    raise OutOfRangeError("number out of range (must be 0..3999)")
              +    raise OutOfRangeError('number out of range (must be 0..3999)')
               roman9.OutOfRangeError: number out of range (must be 0..3999)
               
               ----------------------------------------------------------------------
              @@ -229,7 +229,7 @@ FAILED (errors=3)

              [download roman9.py]

              
              -roman_numeral_pattern = re.compile("""
              +roman_numeral_pattern = re.compile('''
                   ^                   # beginning of string
                   M{0,4}              # thousands - 0 to 4 M's  
                   (CM|CD|D?C{0,3})    # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's),
              @@ -239,16 +239,16 @@ roman_numeral_pattern = re.compile("""
                   (IX|IV|V?I{0,3})    # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's),
                                       #        or 5-8 (V, followed by 0 to 3 I's)
                   $                   # end of string
              -    """, re.VERBOSE)
              +    ''', re.VERBOSE)
               
               def to_roman(n):
              -    """convert integer to Roman numeral"""
              +    '''convert integer to Roman numeral'''
                   if not (0 < n < 5000):                        
              -        raise OutOfRangeError("number out of range (must be 0..4999)")
              +        raise OutOfRangeError('number out of range (must be 0..4999)')
                   if not isinstance(n, int):
              -        raise NotIntegerError("non-integers can not be converted")
              +        raise NotIntegerError('non-integers can not be converted')
               
              -    result = ""
              +    result = ''
                   for numeral, integer in roman_numeral_map:
                       while n >= integer:
                           result += numeral
              @@ -299,7 +299,7 @@ Ran 12 tests in 0.203s
               
               

              Refactoring is the process of taking working code and making it work better. Usually, “better” means “faster”, although it can also mean “using less memory”, or “using less disk space”, or simply “more elegantly”. Whatever it means to you, to your project, in your environment, refactoring is important to the long-term health of any program. -

              Here, “better” means both “faster” and “easier to maintain.” Specifically, the from_roman() function is slower and more complex than I’d like, because of that big nasty regular expression that you use to validate Roman numerals. Now, you might think, "Sure, the regular expression is big and hairy, but how else am I supposed to validate that an arbitrary string is a valid a Roman numeral?" +

              Here, “better” means both “faster” and “easier to maintain.” Specifically, the from_roman() function is slower and more complex than I’d like, because of that big nasty regular expression that you use to validate Roman numerals. Now, you might think, “Sure, the regular expression is big and hairy, but how else am I supposed to validate that an arbitrary string is a valid a Roman numeral?”

              Answer: there’s only 5000 of them; why don’t you just build a lookup table? This idea gets even better when you realize that you don’t need to use regular expressions at all. As you build the lookup table for converting integers to Roman numerals, you can build the reverse lookup table to convert Roman numerals to integers. By the time you need to check whether an arbitrary string is a valid Roman numeral, you will have collected all the valid Roman numerals. “Validating” is reduced to a single dictionary lookup. @@ -328,26 +328,26 @@ to_roman_table = [ None ] from_roman_table = {} def to_roman(n): - """convert integer to Roman numeral""" + '''convert integer to Roman numeral''' if not (0 < n < 5000): - raise OutOfRangeError("number out of range (must be 1..4999)") + raise OutOfRangeError('number out of range (must be 1..4999)') if int(n) != n: - raise NotIntegerError("non-integers can not be converted") + raise NotIntegerError('non-integers can not be converted') return to_roman_table[n] def from_roman(s): - """convert Roman numeral to integer""" + '''convert Roman numeral to integer''' if not isinstance(s, str): - raise InvalidRomanNumeralError("Input must be a string") + raise InvalidRomanNumeralError('Input must be a string') if not s: - raise InvalidRomanNumeralError("Input can not be blank") + raise InvalidRomanNumeralError('Input can not be blank') if s not in from_roman_table: - raise InvalidRomanNumeralError("Invalid Roman numeral: {0}".format(s)) + raise InvalidRomanNumeralError('Invalid Roman numeral: {0}'.format(s)) return from_roman_table[s] def build_lookup_tables(): def to_roman(n): - result = "" + result = '' for numeral, integer in roman_numeral_map: if n >= integer: result = numeral @@ -379,7 +379,7 @@ from_roman_table = {} . def build_lookup_tables(): def to_roman(n): - result = "" + result = '' for numeral, integer in roman_numeral_map: if n >= integer: result = numeral @@ -402,21 +402,21 @@ def build_lookup_tables():

              Once the lookup tables are built, the rest of the code is both easy and fast.

              def to_roman(n):
              -    """convert integer to Roman numeral"""
              +    '''convert integer to Roman numeral'''
                   if not (0 < n < 5000):
              -        raise OutOfRangeError("number out of range (must be 1..4999)")
              +        raise OutOfRangeError('number out of range (must be 1..4999)')
                   if int(n) != n:
              -        raise NotIntegerError("non-integers can not be converted")
              +        raise NotIntegerError('non-integers can not be converted')
                   return to_roman_table[n]                                            
               
               def from_roman(s):
              -    """convert Roman numeral to integer"""
              +    '''convert Roman numeral to integer'''
                   if not isinstance(s, str):
              -        raise InvalidRomanNumeralError("Input must be a string")
              +        raise InvalidRomanNumeralError('Input must be a string')
                   if not s:
              -        raise InvalidRomanNumeralError("Input can not be blank")
              +        raise InvalidRomanNumeralError('Input can not be blank')
                   if s not in from_roman_table:
              -        raise InvalidRomanNumeralError("Invalid Roman numeral: {0}".format(s))
              +        raise InvalidRomanNumeralError('Invalid Roman numeral: {0}'.format(s))
                   return from_roman_table[s]                                          
              1. After doing the same bounds checking as before, the to_roman() function simply finds the appropriate value in the lookup table and returns it. @@ -473,6 +473,7 @@ OK
            2. Refactoring mercilessly to improve performance, scalability, readability, maintainability, or whatever other -ility you’re lacking +

              © 2001–9 Mark Pilgrim diff --git a/regular-expressions.html b/regular-expressions.html index 5e90f71..32c6e29 100644 --- a/regular-expressions.html +++ b/regular-expressions.html @@ -23,7 +23,7 @@ body{counter-reset:h1 4}

              Every modern programming language has built-in functions for working with strings. In Python, strings have methods for searching and replacing: index(), find(), split(), count(), replace(), &c. But these methods are limited to the simplest of cases. For example, the index() method looks for a single, hard-coded substring, and the search is always case-sensitive. To do case-insensitive searches of a string s, you must call s.lower() or s.upper() and make sure your search strings are the appropriate case to match. The replace() and split() methods have the same limitations.

              If your goal can be accomplished with string methods, you should use them. They’re fast and simple and easy to read, and there’s a lot to be said for fast, simple, readable code. But if you find yourself using a lot of different string functions with if statements to handle special cases, or if you’re chaining calls to split() and join() to slice-and-dice your strings, you may need to move up to regular expressions.

              Regular expressions are a powerful and (mostly) standardized way of searching, replacing, and parsing text with complex patterns of characters. Although the regular expression syntax is tight and unlike normal code, the result can end up being more readable than a hand-rolled solution that uses a long chain of string functions. There are even ways of embedding comments within regular expressions, so you can include fine-grained documentation within them. -

              +

              If you’ve used regular expressions in other languages (like Perl 5), Python’s syntax will be very familiar. Read the summary of the re module to get an overview of the available functions and their arguments.

              ⁂ @@ -257,7 +257,7 @@ body{counter-reset:h1 4}

              This will be more clear with an example. Let’s revisit the compact regular expression you’ve been working with, and make it a verbose regular expression. This example shows how.

              ->>> pattern = """
              +>>> pattern = '''
                   ^                   # beginning of string
                   M{0,3}              # thousands - 0 to 3 M's
                   (CM|CD|D?C{0,3})    # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's),
              @@ -267,7 +267,7 @@ body{counter-reset:h1 4}
                   (IX|IV|V?I{0,3})    # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's),
                                       #        or 5-8 (V, followed by 0 to 3 I's)
                   $                   # end of string
              -    """
              +    '''
               >>> re.search(pattern, 'M', re.VERBOSE)                 
               <_sre.SRE_Match object at 0x008EEB48>
               >>> re.search(pattern, 'MCMLXXXIX', re.VERBOSE)         
              @@ -433,7 +433,7 @@ body{counter-reset:h1 4}
               
            3. (x) in general is a remembered group. You can get the value of what matched by using the groups() method of the object returned by re.search.

              Regular expressions are extremely powerful, but they are not the correct solution for every problem. You should learn enough about them to know when they are appropriate, when they will solve your problems, and when they will cause more problems than they solve. -

              © 2001–9 Mark Pilgrim diff --git a/special-method-names.html b/special-method-names.html index 24d46fb..10394f1 100644 --- a/special-method-names.html +++ b/special-method-names.html @@ -5,9 +5,9 @@ diff --git a/where-to-go-from-here.html b/where-to-go-from-here.html index a782617..ad71b1a 100644 --- a/where-to-go-from-here.html +++ b/where-to-go-from-here.html @@ -36,7 +36,7 @@ body{counter-reset:h1 20}

              @@ -55,15 +55,15 @@ body{counter-reset:h1 20}

              As Python 3 is relatively new, there is a dearth of compatible libraries. Here are some of the places to look for code that works with Python 3.

              -

              © 2001–9 Mark Pilgrim diff --git a/xml.html b/xml.html index db1cb4c..00e4bf5 100644 --- a/xml.html +++ b/xml.html @@ -17,7 +17,7 @@ mark{display:inline}

              Difficulty level: ♦♦♦♦♢

              XML

              -

              In the archonship of Aristaechmus, Draco enacted his ordinances.
              Aristotle +

              In the archonship of Aristaechmus, Draco enacted his ordinances.
              Aristotle

               

              Diving In

              @@ -26,29 +26,29 @@ mark{display:inline}

              Here, then, is the XML data we’ll be working with in this chapter. It’s a feed — specifically, an Atom syndication feed.

              [download feed.xml] -

              <?xml version="1.0" encoding="utf-8"?>
              -<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
              +
              <?xml version='1.0' encoding='utf-8'?>
              +<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'>
                 <title>dive into mark</title>
                 <subtitle>currently between addictions</subtitle>
                 <id>tag:diveintomark.org,2001-07-29:/</id>
                 <updated>2009-03-27T21:56:07Z</updated>
              -  <link rel="alternate" type="text/html" href="http://diveintomark.org/"/>
              -  <link rel="self" type="application/atom+xml" href="http://diveintomark.org/feed/"/>
              +  <link rel='alternate' type='text/html' href='http://diveintomark.org/'/>
              +  <link rel='self' type='application/atom+xml' href='http://diveintomark.org/feed/'/>
                 <entry>
                   <author>
                     <name>Mark</name>
                     <uri>http://diveintomark.org/</uri>
                   </author>
                   <title>Dive into history, 2009 edition</title>
              -    <link rel="alternate" type="text/html"
              -      href="http://diveintomark.org/archives/2009/03/27/dive-into-history-2009-edition"/>
              +    <link rel='alternate' type='text/html'
              +      href='http://diveintomark.org/archives/2009/03/27/dive-into-history-2009-edition'/>
                   <id>tag:diveintomark.org,2009-03-27:/archives/20090327172042</id>
                   <updated>2009-03-27T21:56:07Z</updated>
                   <published>2009-03-27T17:20:42Z</published>
              -    <category scheme="http://diveintomark.org" term="diveintopython"/>
              -    <category scheme="http://diveintomark.org" term="docbook"/>
              -    <category scheme="http://diveintomark.org" term="html"/>
              -  <summary type="html">Putting an entire chapter on one page sounds
              +    <category scheme='http://diveintomark.org' term='diveintopython'/>
              +    <category scheme='http://diveintomark.org' term='docbook'/>
              +    <category scheme='http://diveintomark.org' term='html'/>
              +  <summary type='html'>Putting an entire chapter on one page sounds
                   bloated, but consider this &amp;mdash; my longest chapter so far
                   would be 75 printed pages, and it loads in under 5 seconds&amp;hellip;
                   On dialup.</summary>
              @@ -59,13 +59,13 @@ mark{display:inline}
                     <uri>http://diveintomark.org/</uri>
                   </author>
                   <title>Accessibility is a harsh mistress</title>
              -    <link rel="alternate" type="text/html"
              -      href="http://diveintomark.org/archives/2009/03/21/accessibility-is-a-harsh-mistress"/>
              +    <link rel='alternate' type='text/html'
              +      href='http://diveintomark.org/archives/2009/03/21/accessibility-is-a-harsh-mistress'/>
                   <id>tag:diveintomark.org,2009-03-21:/archives/20090321200928</id>
                   <updated>2009-03-22T01:05:37Z</updated>
                   <published>2009-03-21T20:09:28Z</published>
              -    <category scheme="http://diveintomark.org" term="accessibility"/>
              -    <summary type="html">The accessibility orthodoxy does not permit people to
              +    <category scheme='http://diveintomark.org' term='accessibility'/>
              +    <summary type='html'>The accessibility orthodoxy does not permit people to
                     question the value of features that are rarely useful and rarely used.</summary>
                 </entry>
                 <entry>
              @@ -73,20 +73,20 @@ mark{display:inline}
                     <name>Mark</name>
                   </author>
                   <title>A gentle introduction to video encoding, part 1: container formats</title>
              -    <link rel="alternate" type="text/html"
              -      href="http://diveintomark.org/archives/2008/12/18/give-part-1-container-formats"/>
              +    <link rel='alternate' type='text/html'
              +      href='http://diveintomark.org/archives/2008/12/18/give-part-1-container-formats'/>
                   <id>tag:diveintomark.org,2008-12-18:/archives/20081218155422</id>
                   <updated>2009-01-11T19:39:22Z</updated>
                   <published>2008-12-18T15:54:22Z</published>
              -    <category scheme="http://diveintomark.org" term="asf"/>
              -    <category scheme="http://diveintomark.org" term="avi"/>
              -    <category scheme="http://diveintomark.org" term="encoding"/>
              -    <category scheme="http://diveintomark.org" term="flv"/>
              -    <category scheme="http://diveintomark.org" term="GIVE"/>
              -    <category scheme="http://diveintomark.org" term="mp4"/>
              -    <category scheme="http://diveintomark.org" term="ogg"/>
              -    <category scheme="http://diveintomark.org" term="video"/>
              -    <summary type="html">These notes will eventually become part of a
              +    <category scheme='http://diveintomark.org' term='asf'/>
              +    <category scheme='http://diveintomark.org' term='avi'/>
              +    <category scheme='http://diveintomark.org' term='encoding'/>
              +    <category scheme='http://diveintomark.org' term='flv'/>
              +    <category scheme='http://diveintomark.org' term='GIVE'/>
              +    <category scheme='http://diveintomark.org' term='mp4'/>
              +    <category scheme='http://diveintomark.org' term='ogg'/>
              +    <category scheme='http://diveintomark.org' term='video'/>
              +    <summary type='html'>These notes will eventually become part of a
                     tech talk on video encoding.</summary>
                 </entry>
               </feed>
              @@ -120,8 +120,8 @@ mark{display:inline}

              Elements can have attributes, which are name-value pairs. Attributes are listed within the start tag of an element and separated by whitespace. Attribute names can not be repeated within an element. Attribute values must be quoted. -

              <foo lang="en">          
              -  <bar lang="fr"></bar>  
              +
              <foo lang='en'>          
              +  <bar lang='fr'></bar>  
               </foo>
               
                @@ -133,8 +133,8 @@ mark{display:inline}

                Elements can have text content. -

                <foo lang="en">
                -  <bar lang="fr">PapayaWhip</bar>
                +
                <foo lang='en'>
                +  <bar lang='fr'>PapayaWhip</bar>
                 </foo>
                 
                @@ -148,7 +148,7 @@ mark{display:inline}

                Like Python functions can be declared in different modules, XML elements can be declared in different namespaces. Namespaces usually look like URLs. You use an xmlns declaration to define a default namespace. A namespace declaration looks similar to an attribute, but it has a different purpose. -

                <feed xmlns="http://www.w3.org/2005/Atom">  
                +
                <feed xmlns='http://www.w3.org/2005/Atom'>  
                   <title>dive into mark</title>             
                 </feed>
                 
                @@ -159,7 +159,7 @@ mark{display:inline}

                You can also use an xmlns:prefix declaration to define a namespace and associate it with a prefix. Then each element in that namespace must be explicitly declared with the prefix. -

                <atom:feed xmlns:atom="http://www.w3.org/2005/Atom">  
                +
                <atom:feed xmlns:atom='http://www.w3.org/2005/Atom'>  
                   <atom:title>dive into mark</atom:title>             
                 </atom:feed>
                  @@ -171,7 +171,7 @@ mark{display:inline}

                  Finally, XML documents can contain character encoding information on the first line, before the root element. (If you’re curious how a document can contain information which needs to be known before the document can be parsed, Section F of the XML specification details how to resolve this Catch-22.) -

                  <?xml version="1.0" encoding="utf-8"?>
                  +
                  <?xml version='1.0' encoding='utf-8'?>

                  And now you know just enough XML to be dangerous! @@ -185,8 +185,8 @@ mark{display:inline}

                  At the top level is the root element, which every Atom feed shares: the feed element in the http://www.w3.org/2005/Atom namespace. -

                  <feed xmlns="http://www.w3.org/2005/Atom"  
                  -      xml:lang="en">                       
                  +
                  <feed xmlns='http://www.w3.org/2005/Atom'  
                  +      xml:lang='en'>                       
                  1. http://www.w3.org/2005/Atom is the Atom namespace.
                  2. Any element can contain an xml:lang attribute, which declares the language of the element and its children. In this case, the xml:lang attribute is declared once on the root element, which means the entire feed is in English. @@ -194,18 +194,18 @@ mark{display:inline}

                    An Atom feed contains several pieces of information about the feed itself. These are declared as children of the root-level feed element. -

                    <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
                    +
                    <feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'>
                       <title>dive into mark</title>                                             
                       <subtitle>currently between addictions</subtitle>                         
                       <id>tag:diveintomark.org,2001-07-29:/</id>                                
                       <updated>2009-03-27T21:56:07Z</updated>                                   
                    -  <link rel="alternate" type="text/html" href="http://diveintomark.org/"/>  
                    + <link rel='alternate' type='text/html' href='http://diveintomark.org/'/>
                    1. The title of this feed is dive into mark.
                    2. The subtitle of this feed is currently between addictions.
                    3. Every feed needs a globally unique identifier. See RFC 4151 for how to create one.
                    4. This feed was last updated on March 27, 2009, at 21:56 GMT. This is usually equivalent to the last-modified date of the most recent article. -
                    5. Now things start to get interesting. This link element has no text content, but it has three attributes: rel, type, and href. The rel value tells you what kind of link this is; rel="alternate" means that this is a link to an alternate representation of this feed. The type="text/html" attribute means that this is a link to an HTML page. And the link target is given in the href attribute. +
                    6. Now things start to get interesting. This link element has no text content, but it has three attributes: rel, type, and href. The rel value tells you what kind of link this is; rel='alternate' means that this is a link to an alternate representation of this feed. The type='text/html' attribute means that this is a link to an HTML page. And the link target is given in the href attribute.

                    Now we know that this is a feed for a site named “dive into mark“ which is available at http://diveintomark.org/ and was last updated on March 27, 2009. @@ -222,15 +222,15 @@ mark{display:inline} <uri>http://diveintomark.org/</uri> </author> <title>Dive into history, 2009 edition</title> - <link rel="alternate" type="text/html" - href="http://diveintomark.org/archives/2009/03/27/dive-into-history-2009-edition"/> + <link rel='alternate' type='text/html' + href='http://diveintomark.org/archives/2009/03/27/dive-into-history-2009-edition'/> <id>tag:diveintomark.org,2009-03-27:/archives/20090327172042</id> <updated>2009-03-27T21:56:07Z</updated> <published>2009-03-27T17:20:42Z</published> - <category scheme="http://diveintomark.org" term="diveintopython"/> - <category scheme="http://diveintomark.org" term="docbook"/> - <category scheme="http://diveintomark.org" term="html"/> - <summary type="html">Putting an entire chapter on one page sounds + <category scheme='http://diveintomark.org' term='diveintopython'/> + <category scheme='http://diveintomark.org' term='docbook'/> + <category scheme='http://diveintomark.org' term='html'/> + <summary type='html'>Putting an entire chapter on one page sounds bloated, but consider this &amp;mdash; my longest chapter so far would be 75 printed pages, and it loads in under 5 seconds&amp;hellip; On dialup.</summary> @@ -242,7 +242,7 @@ mark{display:inline}

                  3. Entries, like feeds, need a unique identifier.
                  4. Entries have two dates: a first-published date (published) and a last-modified date (updated).
                  5. Entries can have an arbitrary number of categories. This article is filed under diveintopython, docbook, and html. -
                  6. The summary element gives a brief summary of the article. (There is also a content element, not shown here, if you want to include the complete article text in your feed.) This summary element has the Atom-specific type="html" attribute, which specifies that this summary is a snippet of HTML, not plain text. This is important, since it has HTML-specific entities in it (&mdash; and &hellip;) which should be rendered as “—” and “…” rather than displayed directly. +
                  7. The summary element gives a brief summary of the article. (There is also a content element, not shown here, if you want to include the complete article text in your feed.) This summary element has the Atom-specific type='html' attribute, which specifies that this summary is a snippet of HTML, not plain text. This is important, since it has HTML-specific entities in it (&mdash; and &hellip;) which should be rendered as “—” and “…” rather than displayed directly.
                  8. Finally, the end tag for the entry element, signaling the end of the metadata for this article.
                  @@ -255,7 +255,7 @@ mark{display:inline}

                  [download feed.xml]

                   >>> import xml.etree.ElementTree as etree    
                  ->>> tree = etree.parse("examples/feed.xml")  
                  +>>> tree = etree.parse('examples/feed.xml')  
                   >>> root = tree.getroot()                    
                   >>> root                                     
                   <Element {http://www.w3.org/2005/Atom}feed at cd1eb0>
                  @@ -319,7 +319,7 @@ mark{display:inline} >>> root[3].attrib {}
                  -
                1. The attrib property is a dictionary of the element’s attributes. The original markup here was <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">. The xml: prefix refers to a built-in namespace that every XML document can use without declaring it. +
                2. The attrib property is a dictionary of the element’s attributes. The original markup here was <feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'>. The xml: prefix refers to a built-in namespace that every XML document can use without declaring it.
                3. The fifth child — [4] in a 0-based list — is the link element.
                4. The link element has three attributes: href, type, and rel.
                5. The fourth child — [3] in a 0-based list — is the updated element. @@ -334,17 +334,17 @@ mark{display:inline}
                   >>> import xml.etree.ElementTree as etree
                  ->>> tree = etree.parse("examples/feed.xml")
                  +>>> tree = etree.parse('examples/feed.xml')
                   >>> root = tree.getroot()
                  ->>> root.findall("{http://www.w3.org/2005/Atom}entry")    
                  +>>> root.findall('{http://www.w3.org/2005/Atom}entry')    
                   [<Element {http://www.w3.org/2005/Atom}entry at e2b4e0>,
                    <Element {http://www.w3.org/2005/Atom}entry at e2b510>,
                    <Element {http://www.w3.org/2005/Atom}entry at e2b540>]
                   >>> root.tag
                   '{http://www.w3.org/2005/Atom}feed'
                  ->>> root.findall("{http://www.w3.org/2005/Atom}feed")     
                  +>>> root.findall('{http://www.w3.org/2005/Atom}feed')     
                   []
                  ->>> root.findall("{http://www.w3.org/2005/Atom}author")   
                  +>>> root.findall('{http://www.w3.org/2005/Atom}author')   
                   []
                  1. The findall() method finds child elements that match a specific query. (More on the query format in a minute.) @@ -353,22 +353,22 @@ mark{display:inline}
                  ->>> tree.findall("{http://www.w3.org/2005/Atom}entry")    
                  +>>> tree.findall('{http://www.w3.org/2005/Atom}entry')    
                   [<Element {http://www.w3.org/2005/Atom}entry at e2b4e0>,
                    <Element {http://www.w3.org/2005/Atom}entry at e2b510>,
                    <Element {http://www.w3.org/2005/Atom}entry at e2b540>]
                  ->>> tree.findall("{http://www.w3.org/2005/Atom}author")   
                  +>>> tree.findall('{http://www.w3.org/2005/Atom}author')   
                   []
                   
                  1. For convenience, the tree object (returned from the etree.parse() function) has several methods that mirror the methods on the root element. The results are the same as if you had called the tree.getroot().findall() method. -
                  2. Perhaps surprisingly, this query does not find the author elements in this document. Why not? Because this is just a shortcut for tree.getroot().findall("{http://www.w3.org/2005/Atom}author"), which means “find all the author elements that are children of the root element.” The author elements are not children of the root element; they’re children of the entry elements. Thus the query doesn’t return any matches. +
                  3. Perhaps surprisingly, this query does not find the author elements in this document. Why not? Because this is just a shortcut for tree.getroot().findall('{http://www.w3.org/2005/Atom}author'), which means “find all the author elements that are children of the root element.” The author elements are not children of the root element; they’re children of the entry elements. Thus the query doesn’t return any matches.

                  There is a way to search for descendant elements, i.e. children, grandchildren, and any element at any nesting level.

                  ->>> all_links = tree.findall("//{http://www.w3.org/2005/Atom}link")  
                  +>>> all_links = tree.findall('//{http://www.w3.org/2005/Atom}link')  
                   >>> all_links
                   [<Element {http://www.w3.org/2005/Atom}link at e181b0>,
                    <Element {http://www.w3.org/2005/Atom}link at e2b570>,
                  @@ -400,7 +400,7 @@ mark{display:inline}
                   
                   
                   # continuing from the previous example
                  ->>> it = tree.getiterator("{http://www.w3.org/2005/Atom}link")  
                  +>>> it = tree.getiterator('{http://www.w3.org/2005/Atom}link')  
                   >>> next(it)                                                    
                   <Element {http://www.w3.org/2005/Atom}link at 122f1b0>
                   >>> next(it)
                  @@ -428,9 +428,9 @@ StopIteration
                   >>> from lxml import etree                   
                  ->>> tree = etree.parse("examples/feed.xml")  
                  +>>> tree = etree.parse('examples/feed.xml')  
                   >>> root = tree.getroot()                    
                  ->>> root.findall("{http://www.w3.org/2005/Atom}entry")  
                  +>>> root.findall('{http://www.w3.org/2005/Atom}entry')  
                   [<Element {http://www.w3.org/2005/Atom}entry at e2b4e0>,
                    <Element {http://www.w3.org/2005/Atom}entry at e2b510>,
                    <Element {http://www.w3.org/2005/Atom}entry at e2b540>]
                  @@ -452,16 +452,16 @@ except ImportError:
                   >>> import lxml.etree                                                                   
                  ->>> tree = lxml.etree.parse("examples/feed.xml")
                  ->>> tree.findall("//{http://www.w3.org/2005/Atom}*[@href]")                             
                  +>>> tree = lxml.etree.parse('examples/feed.xml')
                  +>>> tree.findall('//{http://www.w3.org/2005/Atom}*[@href]')                             
                   [<Element {http://www.w3.org/2005/Atom}link at eeb8a0>,
                    <Element {http://www.w3.org/2005/Atom}link at eeb990>,
                    <Element {http://www.w3.org/2005/Atom}link at eeb960>,
                    <Element {http://www.w3.org/2005/Atom}link at eeb9c0>]
                   >>> tree.findall("//{http://www.w3.org/2005/Atom}*[@href='http://diveintomark.org/']")  
                   [<Element {http://www.w3.org/2005/Atom}link at eeb930>]
                  ->>> NS = "{http://www.w3.org/2005/Atom}"
                  ->>> tree.findall("//{NS}author[{NS}uri]".format(NS=NS))                                 
                  +>>> NS = '{http://www.w3.org/2005/Atom}'
                  +>>> tree.findall('//{NS}author[{NS}uri]'.format(NS=NS))                                 
                   [<Element {http://www.w3.org/2005/Atom}author at eeba80>,
                    <Element {http://www.w3.org/2005/Atom}author at eebba0>]
                    @@ -475,18 +475,18 @@ except ImportError:
                     >>> import lxml.etree
                    ->>> tree = lxml.etree.parse("examples/feed.xml")
                    ->>> NSMAP = {"atom": "http://www.w3.org/2005/Atom"}                    
                    +>>> tree = lxml.etree.parse('examples/feed.xml')
                    +>>> NSMAP = {'atom': 'http://www.w3.org/2005/Atom'}                    
                     >>> entries = tree.xpath("//atom:category[@term='accessibility']/..",  
                     ...     namespaces=NSMAP)
                     >>> entries                                                            
                     [<Element {http://www.w3.org/2005/Atom}entry at e2b630>]
                     >>> entry = entries[0]
                    ->>> entry.xpath("./atom:title/text()", namespaces=nsmap)               
                    +>>> entry.xpath('./atom:title/text()', namespaces=nsmap)               
                     ['Accessibility is a harsh mistress']
                    1. To perform XPath queries on namespaced elements, you need to define a namespace prefix mapping. This is just a Python dictionary. -
                    2. Here is an XPath query. The XPath expression searches for category elements (in the Atom namespace) that contain a term attribute with the value accessibility. But that’s not actually the query result. Look at the very end of the query string; did you notice the /.. bit? That means “and then return the parent element of the category element you just found.” So this single XPath query will find all entries with a child element of <category term="accessibility">. +
                    3. Here is an XPath query. The XPath expression searches for category elements (in the Atom namespace) that contain a term attribute with the value accessibility. But that’s not actually the query result. Look at the very end of the query string; did you notice the /.. bit? That means “and then return the parent element of the category element you just found.” So this single XPath query will find all entries with a child element of <category term='accessibility'>.
                    4. The xpath() function returns a list of ElementTree objects. In this document, there is only one entry with a category whose term is accessibility.
                    5. XPath expressions don’t always return a list of elements. Technically, the DOM of a parsed XML document doesn’t contain elements; it contains nodes. Depending on their type, nodes can be elements, attributes, or even text content. The result of an XPath query is a list of nodes. This query returns a list of text nodes: the text content (text()) of the title element (atom:title) that is a child of the current element (./).
                    @@ -499,25 +499,25 @@ except ImportError:
                     >>> import xml.etree.ElementTree as etree
                    ->>> new_feed = etree.Element("{http://www.w3.org/2005/Atom}feed",     
                    -...     attrib={"{http://www.w3.org/XML/1998/namespace}lang": "en"})  
                    +>>> new_feed = etree.Element('{http://www.w3.org/2005/Atom}feed',     
                    +...     attrib={'{http://www.w3.org/XML/1998/namespace}lang': 'en'})  
                     >>> print(etree.tostring(new_feed))                                   
                    -<ns0:feed xmlns:ns0="http://www.w3.org/2005/Atom" xml:lang="en"/>
                    +<ns0:feed xmlns:ns0='http://www.w3.org/2005/Atom' xml:lang='en'/>
                  1. To create a new element, instantiate the Element class. You pass the element name (namespace + local name) as the first argument. This statement creates a feed element in the Atom namespace. This will be our new document’s root element.
                  2. To add attributes to the newly created element, pass a dictionary of attribute names and values in the attrib argument. Note that the attribute name should be in the standard ElementTree format, {namespace}localname.
                  3. At any time, you can serialize any element (and its children) with the ElementTree tostring() function.
                  -

                  Was that serialization surprising to you? The way ElementTree serializes namespaced XML elements is technically accurate but not optimal. The sample XML document at the beginning of this chapter defined a default namespace (xmlns="http://www.w3.org/2005/Atom"). Defining a default namespace is useful for documents — like Atom feeds — where every element is in the same namespace, because you can declare the namespace once and declare each element with just its local name (<feed>, <link>, <entry>). There is no need to use any prefixes unless you want to declare elements from another namespace. +

                  Was that serialization surprising to you? The way ElementTree serializes namespaced XML elements is technically accurate but not optimal. The sample XML document at the beginning of this chapter defined a default namespace (xmlns='http://www.w3.org/2005/Atom'). Defining a default namespace is useful for documents — like Atom feeds — where every element is in the same namespace, because you can declare the namespace once and declare each element with just its local name (<feed>, <link>, <entry>). There is no need to use any prefixes unless you want to declare elements from another namespace.

                  An XML parser won’t “see” any difference between an XML document with a default namespace and an XML document with a prefixed namespace. The resulting DOM of this serialization: -

                  <ns0:feed xmlns:ns0="http://www.w3.org/2005/Atom" xml:lang="en"/>
                  +
                  <ns0:feed xmlns:ns0='http://www.w3.org/2005/Atom' xml:lang='en'/>

                  is identical to the DOM of this serialization: -

                  <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"/>
                  +
                  <feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'/>

                  The only practical difference is that the second serialization is several characters shorter. If we were to recast our entire sample feed with a ns0: prefix in every start and end tag, it would add 4 characters per start tag × 79 tags + 4 characters for the namespace declaration itself, for a total of 316 characters. Assuming UTF-8 encoding, that’s 316 extra bytes. (After gzipping, the difference drops to 21 bytes, but still, 21 bytes is 21 bytes.) Maybe that doesn’t matter to you, but for something like an Atom feed, which may be downloaded several thousand times whenever it changes, saving a few bytes per request can quickly add up. @@ -525,13 +525,13 @@ except ImportError:

                   >>> import lxml.etree
                  ->>> NSMAP = {None: "http://www.w3.org/2005/Atom"}                     
                  ->>> new_feed = lxml.etree.Element("feed", nsmap=NSMAP)                
                  +>>> NSMAP = {None: 'http://www.w3.org/2005/Atom'}                     
                  +>>> new_feed = lxml.etree.Element('feed', nsmap=NSMAP)                
                   >>> print(lxml.etree.tounicode(new_feed))                             
                  -<feed xmlns="http://www.w3.org/2005/Atom"/>
                  ->>> new_feed.set("{http://www.w3.org/XML/1998/namespace}lang", "en")  
                  +<feed xmlns='http://www.w3.org/2005/Atom'/>
                  +>>> new_feed.set('{http://www.w3.org/XML/1998/namespace}lang', 'en')  
                   >>> print(lxml.etree.tounicode(new_feed))
                  -<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"/>
                  +<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'/>
                1. To start, define a namespace mapping as a dictionary. Dictionary values are namespaces; dictionary keys are the desired prefix. Using None as a prefix effectively declares a default namespace.
                2. Now you can pass the lxml-specific nsmap argument when you create an element, and lxml will respect the namespace prefixes you’ve defined. @@ -542,16 +542,16 @@ except ImportError:

                  Are XML documents limited to one element per document? No, of course not. You can easily create child elements, too.

                  ->>> title = lxml.etree.SubElement(new_feed, "title",          
                  -...     attrib={"type":"html"})                               
                  +>>> title = lxml.etree.SubElement(new_feed, 'title',          
                  +...     attrib={'type':'html'})                               
                   >>> print(lxml.etree.tounicode(new_feed))
                  -<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><title type="html"/></feed>
                  ->>> title.text = "dive into &hellip;"                         
                  +<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'><title type='html'/></feed>
                  +>>> title.text = 'dive into &hellip;'                         
                   >>> print(lxml.etree.tounicode(new_feed))                     
                  -<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><title type="html">dive into &amp;hellip;</title></feed>
                  +<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'><title type='html'>dive into &amp;hellip;</title></feed>
                   >>> print(lxml.etree.tounicode(new_feed, pretty_print=True))  
                  -<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
                  -<title type="html">dive into&amp;hellip;</title>
                  +<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'>
                  +<title type='html'>dive into&amp;hellip;</title>
                   </feed>
                  1. To create a child element of an existing element, instantiate the SubElement class. The only required arguments are the parent element (new_feed in this case) and the new element’s name. Since this child element will inherit the namespace mapping of its parent, there is no need to redeclare the namespace or prefix here. @@ -574,8 +574,8 @@ except ImportError:

                    Here is a fragment of a broken XML document. I’ve highlighted the wellformedness error. -

                    <?xml version="1.0" encoding="utf-8"?>
                    -<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
                    +
                    <?xml version='1.0' encoding='utf-8'?>
                    +<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'>
                       <title>dive into </title>
                     ...
                     </feed>
                    @@ -584,7 +584,7 @@ except ImportError:
                     >>> import lxml.etree
                    ->>> tree = lxml.etree.parse("examples/feed-broken.xml")
                    +>>> tree = lxml.etree.parse('examples/feed-broken.xml')
                     Traceback (most recent call last):
                       File "<stdin>", line 1, in <module>
                       File "lxml.etree.pyx", line 2693, in lxml.etree.parse (src/lxml/lxml.etree.c:52591)
                    @@ -601,16 +601,16 @@ lxml.etree.XMLSyntaxError: Entity 'hellip' not defined, line 3, column 28
                     
                     
                     >>> parser = lxml.etree.XMLParser(recover=True)                  
                    ->>> tree = lxml.etree.parse("examples/feed-broken.xml", parser)  
                    +>>> tree = lxml.etree.parse('examples/feed-broken.xml', parser)  
                     >>> parser.error_log                                             
                     examples/feed-broken.xml:3:28:FATAL:PARSER:ERR_UNDECLARED_ENTITY: Entity 'hellip' not defined
                    ->>> tree.findall("{http://www.w3.org/2005/Atom}title")
                    +>>> tree.findall('{http://www.w3.org/2005/Atom}title')
                     [<Element {http://www.w3.org/2005/Atom}title at ead510>]
                    ->>> title = tree.findall("{http://www.w3.org/2005/Atom}title")[0]
                    +>>> title = tree.findall('{http://www.w3.org/2005/Atom}title')[0]
                     >>> title.text                                                   
                     'dive into '
                     >>> print(lxml.etree.tounicode(tree.getroot()))                  
                    -<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
                    +<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'>
                       <title>dive into </title>
                     .
                     . [rest of serialization snipped for brevity]
                    @@ -619,7 +619,7 @@ lxml.etree.XMLSyntaxError: Entity 'hellip' not defined, line 3, column 28
                     
                  2. To create a custom parser, instantiate the lxml.etree.XMLParser class. It can take a number of different named arguments. The one we’re interested in here is the recover argument. When set to True, the XML parser will try its best to “recover” from wellformedness errors.
                  3. To parse an XML document with your custom parser, pass the parser object as the second argument to the parse() function. Note that lxml does not raise an exception about the undefined &hellip; entity.
                  4. The parser keeps a log of the wellformedness errors that it has encountered. (This is actually true regardless of whether it is set to recover from those errors or not.) -
                  5. Since it didn’t know what to do with the undefined &hellip; entity, the parser just silently dropped it. The text content of the title element becomes "dive into ". +
                  6. Since it didn’t know what to do with the undefined &hellip; entity, the parser just silently dropped it. The text content of the title element becomes 'dive into '.
                  7. As you can see from the serialization, the &hellip; entity didn’t get moved; it was simply dropped.
                  @@ -640,6 +640,7 @@ lxml.etree.XMLSyntaxError: Entity 'hellip' not defined, line 3, column 28
                3. XPath and XSLT with lxml +

                  © 2001–9 Mark Pilgrim diff --git a/your-first-python-program.html b/your-first-python-program.html index dc80e12..56b399a 100644 --- a/your-first-python-program.html +++ b/your-first-python-program.html @@ -29,7 +29,7 @@ th{text-align:left} 1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']} def approximate_size(size, a_kilobyte_is_1024_bytes=True): - """Convert a file size to human-readable form. + '''Convert a file size to human-readable form. Keyword arguments: size -- file size in bytes @@ -38,7 +38,7 @@ def approximate_size(size, a_kilobyte_is_1024_bytes=True): Returns: string - """ + ''' if size < 0: raise ValueError('number must be non-negative') @@ -46,11 +46,11 @@ def approximate_size(size, a_kilobyte_is_1024_bytes=True): for suffix in SUFFIXES[multiple]: size /= multiple if size < multiple: - return "{0:.1f} {1}".format(size, suffix) + return '{0:.1f} {1}'.format(size, suffix) raise ValueError('number too large') -if __name__ == "__main__": +if __name__ == '__main__': print(approximate_size(1000000000000, False)) print(approximate_size(1000000000000))

                Now let’s run this program on the command line. On Windows, it will look something like this: @@ -82,7 +82,7 @@ if __name__ == "__main__":

                In some languages, functions (that return a value) start with function, and subroutines (that do not return a value) start with sub. There are no subroutines in Python. Everything is a function, all functions return a value (even if it’s None), and all functions start with def.

            4. The approximate_size() function takes the two arguments — size and a_kilobyte_is_1024_bytes — but neither argument specifies a datatype. In Python, variables are never explicitly typed. Python figures out what type a variable is and keeps track of it internally. -

              +

              In Java and other statically-typed languages, you must specify the datatype of the function return value and each function argument. In Python, you never explicitly specify the datatype of anything. Based on what value you assign, Python keeps track of the datatype internally.

              @@ -99,7 +99,7 @@ if __name__ == "__main__":

              Now look at the bottom of the script:

              
              -if __name__ == "__main__":
              +if __name__ == '__main__':
                   print(approximate_size(1000000000000, False))  
                   print(approximate_size(1000000000000))         
                @@ -138,7 +138,7 @@ SyntaxError: non-keyword arg after keyword arg

                Documentation Strings

                You can document a Python function by giving it a documentation string (docstring for short). In this program, the approximate_size() function has a docstring:

                def approximate_size(size, a_kilobyte_is_1024_bytes=True):
                -    """Convert a file size to human-readable form.
                +    '''Convert a file size to human-readable form.
                 
                     Keyword arguments:
                     size -- file size in bytes
                @@ -147,10 +147,10 @@ SyntaxError: non-keyword arg after keyword arg
                Returns: string - """ + '''

                Triple quotes signify a multi-line string. Everything between the start and end quotes is part of a single string, including carriage returns, leading white space, and other quote characters. You can use them anywhere, but you’ll see them most often used when defining a docstring. -

                +

                Triple quotes are also an easy way to define a string with both single and double quotes, like qq/.../ in Perl 5.

                Everything between the triple quotes is the function’s docstring, which documents what the function does. A docstring, if it exists, must be the first thing defined in a function (that is, on the next line after the function declaration). You don’t technically need to give your function a docstring, but you always should. I know you’ve heard this in every programming class you’ve ever taken, but Python gives you an added incentive: the docstring is available at runtime as an attribute of the function. @@ -182,7 +182,7 @@ SyntaxError: non-keyword arg after keyword arg

              1. When you want to use functions defined in imported modules, you need to include the module name. So you can’t just say approximate_size; it must be humansize.approximate_size. If you’ve used classes in Java, this should feel vaguely familiar.
              2. Instead of calling the function as you would expect to, you asked for one of the function’s attributes, __doc__.
              -
              +

              import in Python is like require in Perl. Once you import a Python module, you access its functions with module.function; once you require a Perl module, you access its functions with module::function.

              The import Search Path

              @@ -212,7 +212,7 @@ SyntaxError: non-keyword arg after keyword arg
              1. Importing the sys module makes all of its functions and attributes available.
              2. sys.path is a list of directory names that constitute the current search path. (Yours will look different, depending on your operating system, what version of Python you’re running, and where it was originally installed.) Python will look through these directories (in this order) for a .py file whose name matches what you’re trying to import. -
              3. Actually, I lied; the truth is more complicated than that, because not all modules are stored as .py files. Some, like the sys module, are "built-in modules"; they are actually baked right into Python itself. Built-in modules behave just like regular modules, but their Python source code is not available, because they are not written in Python! (The sys module is written in C.) +
              4. Actually, I lied; the truth is more complicated than that, because not all modules are stored as .py files. Some, like the sys module, are built-in modules; they are actually baked right into Python itself. Built-in modules behave just like regular modules, but their Python source code is not available, because they are not written in Python! (The sys module is written in C.)
              5. You can add a new directory to Python’s search path at runtime by adding the directory name to sys.path, and then Python will look in that directory as well, whenever you try to import a module. The effect lasts as long as Python is running.
              6. By using sys.path.insert(0, new_path), you inserted a new directory as the first item of the sys.path list, and therefore at the beginning of Python’s search path. This is almost always what you want. In case of naming conflicts (for example, if Python ships with version 2 of a particular library but you want to use version 3), this ensures that your modules will be found and used instead of the modules that came with Python.
              @@ -234,18 +234,18 @@ SyntaxError: non-keyword arg after keyword arg for suffix in SUFFIXES[multiple]: size /= multiple if size < multiple: - return "{0:.1f} {1}".format(size, suffix) + return '{0:.1f} {1}'.format(size, suffix) raise ValueError('number too large')
                -
              1. Code blocks are defined by their indentation. By "code block," I mean functions, if statements, for loops, while loops, and so forth. Indenting starts a block and unindenting ends it. There are no explicit braces, brackets, or keywords. This means that whitespace is significant, and must be consistent. In this example, the function code is indented four spaces. It doesn’t need to be four spaces, it just needs to be consistent. The first line that is not indented marks the end of the function. +
              2. Code blocks are defined by their indentation. By “code block,” I mean functions, if statements, for loops, while loops, and so forth. Indenting starts a block and unindenting ends it. There are no explicit braces, brackets, or keywords. This means that whitespace is significant, and must be consistent. In this example, the function code is indented four spaces. It doesn’t need to be four spaces, it just needs to be consistent. The first line that is not indented marks the end of the function.
              3. In Python, an if statement is followed by a code block. If the if expression evaluates to true, the indented block is executed, otherwise it falls to the else block (if any). (Note the lack of parentheses around the expression.)
              4. This line is inside the if code block. This raise statement will raise an exception (of type ValueError), but only if size < 0.
              5. This is not the end of the function. Completely blank lines don’t count. They can make the code more readable, but they don’t count as code block delimiters. The function continues on the next line.
              6. The for loop also marks the start of a code block. Code blocks can contain multiple lines, as long as they are all indented the same amount. This for loop has three lines of code in it. There is no other special syntax for multi-line code blocks. Just indent and get on with your life.

              After some initial protests and several snide analogies to Fortran, you will make peace with this and start seeing its benefits. One major benefit is that all Python programs look similar, since indentation is a language requirement and not a matter of style. This makes it easier to read and understand other people’s Python code. -

              +

              Python uses carriage returns to separate statements and a colon and indentation to separate code blocks. C++ and Java use semicolons to separate statements and curly braces to separate code blocks.

              ⁂ @@ -254,10 +254,10 @@ SyntaxError: non-keyword arg after keyword arg

              Python modules are objects and have several useful attributes. You can use this to easily test your modules as you write them, by including a special block of code that executes when you run the Python file on the command line. Take the last few lines of humansize.py:

              
              -if __name__ == "__main__":
              +if __name__ == '__main__':
                   print(approximate_size(1000000000000, False))
                   print(approximate_size(1000000000000))
              -
              +

              Like C, Python uses == for comparison and = for assignment. Unlike C, Python does not support in-line assignment, so there’s no chance of accidentally assigning the value you thought you were comparing.

              So what makes this if statement special? Well, modules are objects, and all modules have a built-in attribute __name__. A module’s __name__ depends on how you’re using the module. If you import the module, then __name__ is the module’s filename, without a directory path or file extension. @@ -280,7 +280,7 @@ if __name__ == "__main__":

            5. PEP 8: Style Guide for Python Code discusses good indentation style.
            6. Python Reference Manual explains what it means to say that everything in Python is an object, because some people are pedants and like to discuss that sort of thing at great length. -

              © 2001–9 Mark Pilgrim