summaryrefslogtreecommitdiff
path: root/Doc/faq/programming.rst
diff options
context:
space:
mode:
Diffstat (limited to 'Doc/faq/programming.rst')
-rw-r--r--Doc/faq/programming.rst217
1 files changed, 159 insertions, 58 deletions
diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst
index 32123de16a..280d5e13d2 100644
--- a/Doc/faq/programming.rst
+++ b/Doc/faq/programming.rst
@@ -4,7 +4,9 @@
Programming FAQ
===============
-.. contents::
+.. only:: html
+
+ .. contents::
General Questions
=================
@@ -212,9 +214,9 @@ Why do lambdas defined in a loop with different values all return the same resul
Assume you use a for loop to define a few different lambdas (or even plain
functions), e.g.::
- squares = []
- for x in range(5):
- squares.append(lambda: x**2)
+ >>> squares = []
+ >>> for x in range(5):
+ ... squares.append(lambda: x**2)
This gives you a list that contains 5 lambdas that calculate ``x**2``. You
might expect that, when called, they would return, respectively, ``0``, ``1``,
@@ -239,9 +241,9 @@ changing the value of ``x`` and see how the results of the lambdas change::
In order to avoid this, you need to save the values in variables local to the
lambdas, so that they don't rely on the value of the global ``x``::
- squares = []
- for x in range(5):
- squares.append(lambda n=x: n**2)
+ >>> squares = []
+ >>> for x in range(5):
+ ... squares.append(lambda n=x: n**2)
Here, ``n=x`` creates a new variable ``n`` local to the lambda and computed
when the lambda is defined so that it has the same value that ``x`` had at
@@ -590,11 +592,11 @@ Comma is not an operator in Python. Consider this session::
Since the comma is not an operator, but a separator between expressions the
above is evaluated as if you had entered::
- >>> ("a" in "b"), "a"
+ ("a" in "b"), "a"
not::
- >>> "a" in ("b", "a")
+ "a" in ("b", "a")
The same is true of the various assignment operators (``=``, ``+=`` etc). They
are not truly operators but syntactic delimiters in assignment statements.
@@ -742,6 +744,7 @@ it from. However, if you need an object with the ability to modify in-place
unicode data, try using a :class:`io.StringIO` object or the :mod:`array`
module::
+ >>> import io
>>> s = "Hello, world"
>>> sio = io.StringIO(s)
>>> sio.getvalue()
@@ -759,7 +762,7 @@ module::
array('u', 'Hello, world')
>>> a[0] = 'y'
>>> print(a)
- array('u', 'yello world')
+ array('u', 'yello, world')
>>> a.tounicode()
'yello, world'
@@ -1058,7 +1061,7 @@ How do I create a multidimensional list?
You probably tried to make a multidimensional array like this::
- A = [[None] * 2] * 3
+ >>> A = [[None] * 2] * 3
This looks correct if you print it::
@@ -1090,7 +1093,7 @@ use a list comprehension::
A = [[None] * w for i in range(h)]
Or, you can use an extension that provides a matrix datatype; `Numeric Python
-<http://numpy.scipy.org/>`_ is the best known.
+<http://www.numpy.org/>`_ is the best known.
How do I apply a method to a sequence of objects?
@@ -1100,38 +1103,101 @@ Use a list comprehension::
result = [obj.method() for obj in mylist]
+.. _faq-augmented-assignment-tuple-error:
-Dictionaries
-============
+Why does a_tuple[i] += ['item'] raise an exception when the addition works?
+---------------------------------------------------------------------------
-How can I get a dictionary to display its keys in a consistent order?
----------------------------------------------------------------------
+This is because of a combination of the fact that augmented assignment
+operators are *assignment* operators, and the difference between mutable and
+immutable objects in Python.
-You can't. Dictionaries store their keys in an unpredictable order, so the
-display order of a dictionary's elements will be similarly unpredictable.
+This discussion applies in general when augmented assignment operators are
+applied to elements of a tuple that point to mutable objects, but we'll use
+a ``list`` and ``+=`` as our exemplar.
-This can be frustrating if you want to save a printable version to a file, make
-some changes and then compare it with some other printed dictionary. In this
-case, use the ``pprint`` module to pretty-print the dictionary; the items will
-be presented in order sorted by the key.
+If you wrote::
-A more complicated solution is to subclass ``dict`` to create a
-``SortedDict`` class that prints itself in a predictable order. Here's one
-simpleminded implementation of such a class::
+ >>> a_tuple = (1, 2)
+ >>> a_tuple[0] += 1
+ Traceback (most recent call last):
+ ...
+ TypeError: 'tuple' object does not support item assignment
- class SortedDict(dict):
- def __repr__(self):
- keys = sorted(self.keys())
- result = ("{!r}: {!r}".format(k, self[k]) for k in keys)
- return "{{{}}}".format(", ".join(result))
+The reason for the exception should be immediately clear: ``1`` is added to the
+object ``a_tuple[0]`` points to (``1``), producing the result object, ``2``,
+but when we attempt to assign the result of the computation, ``2``, to element
+``0`` of the tuple, we get an error because we can't change what an element of
+a tuple points to.
- __str__ = __repr__
+Under the covers, what this augmented assignment statement is doing is
+approximately this::
-This will work for many common situations you might encounter, though it's far
-from a perfect solution. The largest flaw is that if some values in the
-dictionary are also dictionaries, their values won't be presented in any
-particular order.
+ >>> result = a_tuple[0] + 1
+ >>> a_tuple[0] = result
+ Traceback (most recent call last):
+ ...
+ TypeError: 'tuple' object does not support item assignment
+
+It is the assignment part of the operation that produces the error, since a
+tuple is immutable.
+When you write something like::
+
+ >>> a_tuple = (['foo'], 'bar')
+ >>> a_tuple[0] += ['item']
+ Traceback (most recent call last):
+ ...
+ TypeError: 'tuple' object does not support item assignment
+
+The exception is a bit more surprising, and even more surprising is the fact
+that even though there was an error, the append worked::
+
+ >>> a_tuple[0]
+ ['foo', 'item']
+
+To see why this happens, you need to know that (a) if an object implements an
+``__iadd__`` magic method, it gets called when the ``+=`` augmented assignment
+is executed, and its return value is what gets used in the assignment statement;
+and (b) for lists, ``__iadd__`` is equivalent to calling ``extend`` on the list
+and returning the list. That's why we say that for lists, ``+=`` is a
+"shorthand" for ``list.extend``::
+
+ >>> a_list = []
+ >>> a_list += [1]
+ >>> a_list
+ [1]
+
+This is equivalent to::
+
+ >>> result = a_list.__iadd__([1])
+ >>> a_list = result
+
+The object pointed to by a_list has been mutated, and the pointer to the
+mutated object is assigned back to ``a_list``. The end result of the
+assignment is a no-op, since it is a pointer to the same object that ``a_list``
+was previously pointing to, but the assignment still happens.
+
+Thus, in our tuple example what is happening is equivalent to::
+
+ >>> result = a_tuple[0].__iadd__(['item'])
+ >>> a_tuple[0] = result
+ Traceback (most recent call last):
+ ...
+ TypeError: 'tuple' object does not support item assignment
+
+The ``__iadd__`` succeeds, and thus the list is extended, but even though
+``result`` points to the same object that ``a_tuple[0]`` already points to,
+that final assignment still results in an error, because tuples are immutable.
+
+
+Dictionaries
+============
+
+How can I get a dictionary to store and display its keys in a consistent order?
+-------------------------------------------------------------------------------
+
+Use :class:`collections.OrderedDict`.
I want to do a complicated sort: can you do a Schwartzian Transform in Python?
------------------------------------------------------------------------------
@@ -1510,41 +1576,76 @@ You can program the class's constructor to keep track of all instances by
keeping a list of weak references to each instance.
+Why does the result of ``id()`` appear to be not unique?
+--------------------------------------------------------
+
+The :func:`id` builtin returns an integer that is guaranteed to be unique during
+the lifetime of the object. Since in CPython, this is the object's memory
+address, it happens frequently that after an object is deleted from memory, the
+next freshly created object is allocated at the same position in memory. This
+is illustrated by this example:
+
+>>> id(1000)
+13901272
+>>> id(2000)
+13901272
+
+The two ids belong to different integer objects that are created before, and
+deleted immediately after execution of the ``id()`` call. To be sure that
+objects whose id you want to examine are still alive, create another reference
+to the object:
+
+>>> a = 1000; b = 2000
+>>> id(a)
+13901272
+>>> id(b)
+13891296
+
+
Modules
=======
How do I create a .pyc file?
----------------------------
-When a module is imported for the first time (or when the source is more recent
-than the current compiled file) a ``.pyc`` file containing the compiled code
-should be created in the same directory as the ``.py`` file.
-
-One reason that a ``.pyc`` file may not be created is permissions problems with
-the directory. This can happen, for example, if you develop as one user but run
-as another, such as if you are testing with a web server. Creation of a .pyc
-file is automatic if you're importing a module and Python has the ability
-(permissions, free space, etc...) to write the compiled module back to the
-directory.
-
-Running Python on a top level script is not considered an import and no ``.pyc``
-will be created. For example, if you have a top-level module ``abc.py`` that
-imports another module ``xyz.py``, when you run abc, ``xyz.pyc`` will be created
-since xyz is imported, but no ``abc.pyc`` file will be created since ``abc.py``
-isn't being imported.
-
-If you need to create abc.pyc -- that is, to create a .pyc file for a module
-that is not imported -- you can, using the :mod:`py_compile` and
-:mod:`compileall` modules.
+When a module is imported for the first time (or when the source file has
+changed since the current compiled file was created) a ``.pyc`` file containing
+the compiled code should be created in a ``__pycache__`` subdirectory of the
+directory containing the ``.py`` file. The ``.pyc`` file will have a
+filename that starts with the same name as the ``.py`` file, and ends with
+``.pyc``, with a middle component that depends on the particular ``python``
+binary that created it. (See :pep:`3147` for details.)
+
+One reason that a ``.pyc`` file may not be created is a permissions problem
+with the directory containing the source file, meaning that the ``__pycache__``
+subdirectory cannot be created. This can happen, for example, if you develop as
+one user but run as another, such as if you are testing with a web server.
+
+Unless the :envvar:`PYTHONDONTWRITEBYTECODE` environment variable is set,
+creation of a .pyc file is automatic if you're importing a module and Python
+has the ability (permissions, free space, etc...) to create a ``__pycache__``
+subdirectory and write the compiled module to that subdirectory.
+
+Running Python on a top level script is not considered an import and no
+``.pyc`` will be created. For example, if you have a top-level module
+``foo.py`` that imports another module ``xyz.py``, when you run ``foo`` (by
+typing ``python foo.py`` as a shell command), a ``.pyc`` will be created for
+``xyz`` because ``xyz`` is imported, but no ``.pyc`` file will be created for
+``foo`` since ``foo.py`` isn't being imported.
+
+If you need to create a ``.pyc`` file for ``foo`` -- that is, to create a
+``.pyc`` file for a module that is not imported -- you can, using the
+:mod:`py_compile` and :mod:`compileall` modules.
The :mod:`py_compile` module can manually compile any module. One way is to use
the ``compile()`` function in that module interactively::
>>> import py_compile
- >>> py_compile.compile('abc.py')
+ >>> py_compile.compile('foo.py') # doctest: +SKIP
-This will write the ``.pyc`` to the same location as ``abc.py`` (or you can
-override that with the optional parameter ``cfile``).
+This will write the ``.pyc`` to a ``__pycache__`` subdirectory in the same
+location as ``foo.py`` (or you can override that with the optional parameter
+``cfile``).
You can also automatically compile all files in a directory or directories using
the :mod:`compileall` module. You can do it from the shell prompt by running