#13549: improve tutorial section about listcomps.
This commit is contained in:
parent
f537702732
commit
4a72d1a661
|
@ -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]
|
|
||||||
>>> [x+y for x in vec1 for y in vec2]
|
|
||||||
[6, 5, -7, 8, 7, -5, 10, 9, -3]
|
|
||||||
>>> [vec1[i]*vec2[i] for i in range(len(vec1))]
|
|
||||||
[8, 12, -54]
|
|
||||||
|
|
||||||
List comprehensions are much more flexible than :func:`map` and can be applied
|
List comprehensions can contain complex expressions and nested functions::
|
||||||
to complex expressions and nested functions::
|
|
||||||
|
|
||||||
>>> [str(round(355/113.0, i)) for i in range(1,6)]
|
>>> from math import pi
|
||||||
|
>>> [str(round(pi, 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
|
The initial expression in a list comprehension can be any arbitrary expression,
|
||||||
powerful tool but -- like all powerful tools -- they need to be used carefully,
|
including another list comprehension.
|
||||||
if at all.
|
|
||||||
|
|
||||||
Consider the following example of a 3x3 matrix held as a list containing three
|
Consider the following example of a 3x4 matrix implemented as a list of
|
||||||
lists, one list per row::
|
3 lists of length 4::
|
||||||
|
|
||||||
>>> mat = [
|
>>> matrix = [
|
||||||
... [1, 2, 3],
|
... [1, 2, 3, 4],
|
||||||
... [4, 5, 6],
|
... [5, 6, 7, 8],
|
||||||
... [7, 8, 9],
|
... [9, 10, 11, 12],
|
||||||
... ]
|
... ]
|
||||||
|
|
||||||
Now, if you wanted to swap rows and columns, you could use a list
|
The following list comprehension will transpose rows and columns::
|
||||||
comprehension::
|
|
||||||
|
|
||||||
>>> print [[row[i] for row in mat] for i in [0, 1, 2]]
|
>>> [[row[i] for row in matrix] for i in range(4)]
|
||||||
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
|
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
|
||||||
|
|
||||||
Special care has to be taken for the *nested* list comprehension:
|
As we saw in the previous section, the nested listcomp is evaluated in
|
||||||
|
the context of the :keyword:`for` that follows it, so this example is
|
||||||
|
equivalent to::
|
||||||
|
|
||||||
To avoid apprehension when nesting list comprehensions, read from right to
|
>>> transposed = []
|
||||||
left.
|
>>> 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]]
|
||||||
|
|
||||||
A more verbose version of this snippet shows the flow explicitly::
|
which, in turn, is the same as::
|
||||||
|
|
||||||
for i in [0, 1, 2]:
|
>>> transposed = []
|
||||||
for row in mat:
|
>>> for i in range(4):
|
||||||
print row[i],
|
... # the following 3 lines implement the nested listcomp
|
||||||
print
|
... 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]]
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue