Commit 4a72d1a6 authored by Ezio Melotti's avatar Ezio Melotti

#13549: improve tutorial section about listcomps.

parent f5377027
...@@ -235,89 +235,139 @@ and works exactly like this. ...@@ -235,89 +235,139 @@ and works exactly like this.
List Comprehensions List Comprehensions
------------------- -------------------
List comprehensions provide a concise way to create lists without resorting to List comprehensions provide a concise way to create lists.
use of :func:`map`, :func:`filter` and/or :keyword:`lambda`. The resulting list Common applications are to make new lists where each element is the result of
definition tends often to be clearer than lists built using those constructs. some operations applied to each member of another sequence or iterable, or to
Each list comprehension consists of an expression followed by a :keyword:`for` create a subsequence of those elements that satisfy a certain condition.
clause, then zero or more :keyword:`for` or :keyword:`if` clauses. The result
will be a list resulting from evaluating the expression in the context of the
:keyword:`for` and :keyword:`if` clauses which follow it. If the expression
would evaluate to a tuple, it must be parenthesized. ::
For example, assume we want to create a list of squares, like::
>>> squares = []
>>> for x in range(10):
... squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
We can obtain the same result with::
squares = [x**2 for x in range(10)]
This is also equivalent to ``squares = map(lambda x: x**2, range(10))``,
but it's more concise and readable.
A list comprehension consists of brackets containing an expression followed
by a :keyword:`for` clause, then zero or more :keyword:`for` or :keyword:`if`
clauses. The result will be a new list resulting from evaluating the expression
in the context of the :keyword:`for` and :keyword:`if` clauses which follow it.
For example, this listcomp combines the elements of two lists if they are not
equal::
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
and it's equivalent to:
>>> combs = []
>>> for x in [1,2,3]:
... for y in [3,1,4]:
... if x != y:
... combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
Note how the order of the :keyword:`for` and :keyword:`if` statements is the
same in both these snippets.
If the expression is a tuple (e.g. the ``(x, y)`` in the previous example),
it must be parenthesized. ::
>>> vec = [-4, -2, 0, 2, 4]
>>> # create a new list with the values doubled
>>> [x*2 for x in vec]
[-8, -4, 0, 4, 8]
>>> # filter the list to exclude negative numbers
>>> [x for x in vec if x >= 0]
[0, 2, 4]
>>> # apply a function to all the elements
>>> [abs(x) for x in vec]
[4, 2, 0, 2, 4]
>>> # call a method on each element
>>> freshfruit = [' banana', ' loganberry ', 'passion fruit '] >>> freshfruit = [' banana', ' loganberry ', 'passion fruit ']
>>> [weapon.strip() for weapon in freshfruit] >>> [weapon.strip() for weapon in freshfruit]
['banana', 'loganberry', 'passion fruit'] ['banana', 'loganberry', 'passion fruit']
>>> vec = [2, 4, 6] >>> # create a list of 2-tuples like (number, square)
>>> [3*x for x in vec] >>> [(x, x**2) for x in range(6)]
[6, 12, 18] [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
>>> [3*x for x in vec if x > 3] >>> # the tuple must be parenthesized, otherwise an error is raised
[12, 18] >>> [x, x**2 for x in range(6)]
>>> [3*x for x in vec if x < 2] File "<stdin>", line 1
[] [x, x**2 for x in range(6)]
>>> [[x,x**2] for x in vec]
[[2, 4], [4, 16], [6, 36]]
>>> [x, x**2 for x in vec] # error - parens required for tuples
File "<stdin>", line 1, in ?
[x, x**2 for x in vec]
^ ^
SyntaxError: invalid syntax SyntaxError: invalid syntax
>>> [(x, x**2) for x in vec] >>> # flatten a list using a listcomp with two 'for'
[(2, 4), (4, 16), (6, 36)] >>> vec = [[1,2,3], [4,5,6], [7,8,9]]
>>> vec1 = [2, 4, 6] >>> [num for elem in vec for num in elem]
>>> vec2 = [4, 3, -9] [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [x*y for x in vec1 for y in vec2]
[8, 6, -18, 16, 12, -36, 24, 18, -54] List comprehensions can contain complex expressions and nested functions::
>>> [x+y for x in vec1 for y in vec2]
[6, 5, -7, 8, 7, -5, 10, 9, -3] >>> from math import pi
>>> [vec1[i]*vec2[i] for i in range(len(vec1))] >>> [str(round(pi, i)) for i in range(1, 6)]
[8, 12, -54]
List comprehensions are much more flexible than :func:`map` and can be applied
to complex expressions and nested functions::
>>> [str(round(355/113.0, i)) for i in range(1,6)]
['3.1', '3.14', '3.142', '3.1416', '3.14159'] ['3.1', '3.14', '3.142', '3.1416', '3.14159']
Nested List Comprehensions Nested List Comprehensions
-------------------------- ''''''''''''''''''''''''''
If you've got the stomach for it, list comprehensions can be nested. They are a
powerful tool but -- like all powerful tools -- they need to be used carefully,
if at all.
Consider the following example of a 3x3 matrix held as a list containing three The initial expression in a list comprehension can be any arbitrary expression,
lists, one list per row:: including another list comprehension.
>>> mat = [ Consider the following example of a 3x4 matrix implemented as a list of
... [1, 2, 3], 3 lists of length 4::
... [4, 5, 6],
... [7, 8, 9],
... ]
Now, if you wanted to swap rows and columns, you could use a list >>> matrix = [
comprehension:: ... [1, 2, 3, 4],
... [5, 6, 7, 8],
... [9, 10, 11, 12],
... ]
>>> print [[row[i] for row in mat] for i in [0, 1, 2]] The following list comprehension will transpose rows and columns::
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
Special care has to be taken for the *nested* list comprehension: >>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
To avoid apprehension when nesting list comprehensions, read from right to As we saw in the previous section, the nested listcomp is evaluated in
left. the context of the :keyword:`for` that follows it, so this example is
equivalent to::
A more verbose version of this snippet shows the flow explicitly:: >>> transposed = []
>>> for i in range(4):
... transposed.append([row[i] for row in matrix])
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
which, in turn, is the same as::
>>> transposed = []
>>> for i in range(4):
... # the following 3 lines implement the nested listcomp
... transposed_row = []
... for row in matrix:
... transposed_row.append(row[i])
... transposed.append(transposed_row)
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
for i in [0, 1, 2]:
for row in mat:
print row[i],
print
In real world, you should prefer built-in functions to complex flow statements. In the real world, you should prefer built-in functions to complex flow statements.
The :func:`zip` function would do a great job for this use case:: The :func:`zip` function would do a great job for this use case::
>>> zip(*mat) >>> zip(*matrix)
[(1, 4, 7), (2, 5, 8), (3, 6, 9)] [(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]
See :ref:`tut-unpacking-arguments` for details on the asterisk in this line. See :ref:`tut-unpacking-arguments` for details on the asterisk in this line.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment