[Python-checkins] r76886 - python/trunk/Doc/faq/design.rst

georg.brandl python-checkins at python.org
Sat Dec 19 18:43:34 CET 2009


Author: georg.brandl
Date: Sat Dec 19 18:43:33 2009
New Revision: 76886

Log:
#7493: review of Design FAQ by Florent Xicluna.

Modified:
   python/trunk/Doc/faq/design.rst

Modified: python/trunk/Doc/faq/design.rst
==============================================================================
--- python/trunk/Doc/faq/design.rst	(original)
+++ python/trunk/Doc/faq/design.rst	Sat Dec 19 18:43:33 2009
@@ -7,7 +7,7 @@
 
 Guido van Rossum believes that using indentation for grouping is extremely
 elegant and contributes a lot to the clarity of the average Python program.
-Most people learn to love this feature after awhile.
+Most people learn to love this feature after a while.
 
 Since there are no begin/end brackets there cannot be a disagreement between
 grouping perceived by the parser and the human reader.  Occasionally C
@@ -48,7 +48,7 @@
 
 People are often very surprised by results like this::
 
-   >>> 1.2-1.0
+   >>> 1.2 - 1.0
    0.199999999999999996
 
 and think it is a bug in Python. It's not.  This has nothing to do with Python,
@@ -85,7 +85,7 @@
 ``==`` fails.  Instead, you have to check that the difference between the two
 numbers is less than a certain threshold::
 
-   epsilon = 0.0000000000001 # Tiny allowed error
+   epsilon = 0.0000000000001  # Tiny allowed error
    expected_result = 0.4
 
    if expected_result-epsilon <= computation() <= expected_result+epsilon:
@@ -131,24 +131,25 @@
 Second, it means that no special syntax is necessary if you want to explicitly
 reference or call the method from a particular class.  In C++, if you want to
 use a method from a base class which is overridden in a derived class, you have
-to use the ``::`` operator -- in Python you can write baseclass.methodname(self,
-<argument list>).  This is particularly useful for :meth:`__init__` methods, and
-in general in cases where a derived class method wants to extend the base class
-method of the same name and thus has to call the base class method somehow.
+to use the ``::`` operator -- in Python you can write
+``baseclass.methodname(self, <argument list>)``.  This is particularly useful
+for :meth:`__init__` methods, and in general in cases where a derived class
+method wants to extend the base class method of the same name and thus has to
+call the base class method somehow.
 
 Finally, for instance variables it solves a syntactic problem with assignment:
 since local variables in Python are (by definition!) those variables to which a
-value assigned in a function body (and that aren't explicitly declared global),
-there has to be some way to tell the interpreter that an assignment was meant to
-assign to an instance variable instead of to a local variable, and it should
-preferably be syntactic (for efficiency reasons).  C++ does this through
+value is assigned in a function body (and that aren't explicitly declared
+global), there has to be some way to tell the interpreter that an assignment was
+meant to assign to an instance variable instead of to a local variable, and it
+should preferably be syntactic (for efficiency reasons).  C++ does this through
 declarations, but Python doesn't have declarations and it would be a pity having
-to introduce them just for this purpose.  Using the explicit "self.var" solves
+to introduce them just for this purpose.  Using the explicit ``self.var`` solves
 this nicely.  Similarly, for using instance variables, having to write
-"self.var" means that references to unqualified names inside a method don't have
-to search the instance's directories.  To put it another way, local variables
-and instance variables live in two different namespaces, and you need to tell
-Python which namespace to use.
+``self.var`` means that references to unqualified names inside a method don't
+have to search the instance's directories.  To put it another way, local
+variables and instance variables live in two different namespaces, and you need
+to tell Python which namespace to use.
 
 
 Why can't I use an assignment in an expression?
@@ -234,8 +235,10 @@
 
 .. XXX talk about protocols?
 
-Note that for string operations Python has moved from external functions (the
-``string`` module) to methods.  However, ``len()`` is still a function.
+.. note::
+
+   For string operations, Python has moved from external functions (the
+   ``string`` module) to methods.  However, ``len()`` is still a function.
 
 
 Why is join() a string method instead of a list or tuple method?
@@ -298,22 +301,24 @@
 expensive.  In versions of Python prior to 2.0 it was common to use this idiom::
 
    try:
-       value = dict[key]
+       value = mydict[key]
    except KeyError:
-       dict[key] = getvalue(key)
-       value = dict[key]
+       mydict[key] = getvalue(key)
+       value = mydict[key]
 
 This only made sense when you expected the dict to have the key almost all the
 time.  If that wasn't the case, you coded it like this::
 
-   if dict.has_key(key):
-       value = dict[key]
+   if mydict.has_key(key):
+       value = mydict[key]
    else:
-       dict[key] = getvalue(key)
-       value = dict[key]
+       mydict[key] = getvalue(key)
+       value = mydict[key]
+
+.. note::
 
-(In Python 2.0 and higher, you can code this as ``value = dict.setdefault(key,
-getvalue(key))``.)
+   In Python 2.0 and higher, you can code this as ``value =
+   mydict.setdefault(key, getvalue(key))``.
 
 
 Why isn't there a switch or case statement in Python?
@@ -432,7 +437,7 @@
 <http://www.cosc.canterbury.ac.nz/~greg/python/Pyrex/>`_, `PyInline
 <http://pyinline.sourceforge.net/>`_, `Py2Cmod
 <http://sourceforge.net/projects/py2cmod/>`_, and `Weave
-<http://www.scipy.org/site_content/weave>`_.
+<http://www.scipy.org/Weave>`_.
 
 
 How does Python manage memory?
@@ -450,6 +455,8 @@
 difference can cause some subtle porting problems if your Python code depends on
 the behavior of the reference counting implementation.
 
+.. XXX relevant for Python 2.6?
+
 Sometimes objects get stuck in tracebacks temporarily and hence are not
 deallocated when you might expect.  Clear the tracebacks with::
 
@@ -461,8 +468,8 @@
 things.  They contain a portion of the program state extracted during the
 handling of an exception (usually the most recent exception).
 
-In the absence of circularities and tracebacks, Python programs need not
-explicitly manage memory.
+In the absence of circularities and tracebacks, Python programs do not need to
+manage memory explicitly.
 
 Why doesn't Python use a more traditional garbage collection scheme?  For one
 thing, this is not a C standard feature and hence it's not portable.  (Yes, we
@@ -481,19 +488,19 @@
 In Jython, the following code (which is fine in CPython) will probably run out
 of file descriptors long before it runs out of memory::
 
-   for file in <very long list of files>:
+   for file in very_long_list_of_files:
        f = open(file)
        c = f.read(1)
 
 Using the current reference counting and destructor scheme, each new assignment
 to f closes the previous file.  Using GC, this is not guaranteed.  If you want
 to write code that will work with any Python implementation, you should
-explicitly close the file; this will work regardless of GC::
+explicitly close the file or use the :keyword:`with` statement; this will work
+regardless of GC::
 
-   for file in <very long list of files>:
-       f = open(file)
-       c = f.read(1)
-       f.close()
+   for file in very_long_list_of_files:
+       with open(file) as f:
+           c = f.read(1)
 
 
 Why isn't all memory freed when Python exits?
@@ -589,10 +596,10 @@
 - Hash lists by their address (object ID).  This doesn't work because if you
   construct a new list with the same value it won't be found; e.g.::
 
-     d = {[1,2]: '12'}
-     print d[[1,2]]
+     mydict = {[1, 2]: '12'}
+     print mydict[[1, 2]]
 
-  would raise a KeyError exception because the id of the ``[1,2]`` used in the
+  would raise a KeyError exception because the id of the ``[1, 2]`` used in the
   second line differs from that in the first line.  In other words, dictionary
   keys should be compared using ``==``, not using :keyword:`is`.
 
@@ -613,7 +620,7 @@
 
 There is a trick to get around this if you need to, but use it at your own risk:
 You can wrap a mutable structure inside a class instance which has both a
-:meth:`__cmp_` and a :meth:`__hash__` method.  You must then make sure that the
+:meth:`__eq__` and a :meth:`__hash__` method.  You must then make sure that the
 hash value for all such wrapper objects that reside in a dictionary (or other
 hash based structure), remain fixed while the object is in the dictionary (or
 other structure). ::
@@ -621,15 +628,15 @@
    class ListWrapper:
        def __init__(self, the_list):
            self.the_list = the_list
-       def __cmp__(self, other):
+       def __eq__(self, other):
            return self.the_list == other.the_list
        def __hash__(self):
            l = self.the_list
            result = 98767 - len(l)*555
-           for i in range(len(l)):
+           for i, el in enumerate(l):
                try:
-                   result = result + (hash(l[i]) % 9999999) * 1001 + i
-               except:
+                   result = result + (hash(el) % 9999999) * 1001 + i
+               except Exception:
                    result = (result % 7777777) + i * 333
            return result
 
@@ -637,8 +644,8 @@
 members of the list may be unhashable and also by the possibility of arithmetic
 overflow.
 
-Furthermore it must always be the case that if ``o1 == o2`` (ie ``o1.__cmp__(o2)
-== 0``) then ``hash(o1) == hash(o2)`` (ie, ``o1.__hash__() == o2.__hash__()``),
+Furthermore it must always be the case that if ``o1 == o2`` (ie ``o1.__eq__(o2)
+is True``) then ``hash(o1) == hash(o2)`` (ie, ``o1.__hash__() == o2.__hash__()``),
 regardless of whether the object is in a dictionary or not.  If you fail to meet
 these restrictions dictionaries and other hash based structures will misbehave.
 
@@ -661,8 +668,8 @@
 creates a new list from a provided iterable, sorts it and returns it.  For
 example, here's how to iterate over the keys of a dictionary in sorted order::
 
-   for key in sorted(dict.iterkeys()):
-       ... # do whatever with dict[key]...
+   for key in sorted(mydict):
+       ... # do whatever with mydict[key]...
 
 
 How do you specify and enforce an interface spec in Python?
@@ -711,14 +718,14 @@
 
 This type of bug commonly bites neophyte programmers.  Consider this function::
 
-   def foo(D={}):  # Danger: shared reference to one dict for all calls
+   def foo(mydict={}):  # Danger: shared reference to one dict for all calls
        ... compute something ...
-       D[key] = value
-       return D
+       mydict[key] = value
+       return mydict
 
-The first time you call this function, ``D`` contains a single item.  The second
-time, ``D`` contains two items because when ``foo()`` begins executing, ``D``
-starts out with an item already in it.
+The first time you call this function, ``mydict`` contains a single item.  The
+second time, ``mydict`` contains two items because when ``foo()`` begins
+executing, ``mydict`` starts out with an item already in it.
 
 It is often expected that a function call creates new objects for default
 values. This is not what happens. Default values are created exactly once, when
@@ -734,14 +741,14 @@
 inside the function, check if the parameter is ``None`` and create a new
 list/dictionary/whatever if it is.  For example, don't write::
 
-   def foo(dict={}):
+   def foo(mydict={}):
        ...
 
 but::
 
-   def foo(dict=None):
-       if dict is None:
-           dict = {} # create a new dict for local namespace
+   def foo(mydict=None):
+       if mydict is None:
+           mydict = {}  # create a new dict for local namespace
 
 This feature can be useful.  When you have a function that's time-consuming to
 compute, a common technique is to cache the parameters and the resulting value
@@ -750,7 +757,7 @@
 
    # Callers will never provide a third parameter for this function.
    def expensive (arg1, arg2, _cache={}):
-       if _cache.has_key((arg1, arg2)):
+       if (arg1, arg2) in _cache:
            return _cache[(arg1, arg2)]
 
        # Calculate the value
@@ -770,13 +777,13 @@
 reasonable uses of the "go" or "goto" constructs of C, Fortran, and other
 languages.  For example::
 
-   class label: pass # declare a label
+   class label: pass  # declare a label
 
    try:
         ...
-        if (condition): raise label() # goto label
+        if (condition): raise label()  # goto label
         ...
-   except label: # where to goto
+   except label:  # where to goto
         pass
    ...
 
@@ -801,7 +808,7 @@
 If you're trying to build Windows pathnames, note that all Windows system calls
 accept forward slashes too::
 
-   f = open("/mydir/file.txt") # works fine!
+   f = open("/mydir/file.txt")  # works fine!
 
 If you're trying to build a pathname for a DOS command, try e.g. one of ::
 
@@ -849,21 +856,20 @@
 The primary benefit of "with" and similar language features (reduction of code
 volume) can, however, easily be achieved in Python by assignment.  Instead of::
 
-   function(args).dict[index][index].a = 21
-   function(args).dict[index][index].b = 42
-   function(args).dict[index][index].c = 63
+   function(args).mydict[index][index].a = 21
+   function(args).mydict[index][index].b = 42
+   function(args).mydict[index][index].c = 63
 
 write this::
 
-   ref = function(args).dict[index][index]
+   ref = function(args).mydict[index][index]
    ref.a = 21
    ref.b = 42
    ref.c = 63
 
 This also has the side-effect of increasing execution speed because name
 bindings are resolved at run-time in Python, and the second version only needs
-to perform the resolution once.  If the referenced object does not have a, b and
-c attributes, of course, the end result is still a run-time exception.
+to perform the resolution once.
 
 
 Why are colons required for the if/while/def/class statements?


More information about the Python-checkins mailing list