Reword the discussion on short ways to manipulate lists

- Remove emphasis on map and filter, in favor of generator expressions.
 - Move list comprehensions and generator expressions under "Short ways
   to manipulate lists" rather than "Filtering a list".
 - Add some examples.
This commit is contained in:
George Brova
2019-01-15 01:00:13 +01:00
parent 90af77a526
commit c283dbdf9b
+66 -51
View File
@@ -521,7 +521,7 @@ Conventions
Here are some conventions you should follow to make your code easier to read.
Check if a variable equals a constant
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You don't need to explicitly compare a value to True, or None, or 0 -- you can
just add it to the if statement. See `Truth Value Testing
@@ -588,9 +588,70 @@ Short Ways to Manipulate Lists
`List comprehensions
<http://docs.python.org/tutorial/datastructures.html#list-comprehensions>`_
provide a powerful, concise way to work with lists. Also, the :py:func:`map` and
:py:func:`filter` functions can perform operations on lists using a different,
more concise syntax.
provide a powerful, concise way to work with lists.
`Generator expressions
<http://docs.python.org/tutorial/classes.html#generator-expressions>`_
follow almost the same syntax as list comprehensions but return a generator
instead of a list.
Creating a new list requires more work and uses more memory. If you are just going
to loop through the new list, prefer using an iterator instead.
**Bad**:
.. code-block:: python
# needlessly allocates a list of all (gpa, name) entires in memory
valedictorian = max([(student.gpa, student.name) for student in graduates])
**Good**:
.. code-block:: python
valedictorian = max((student.gpa, student.name) for student in graduates)
Use list comprehensions when you really need to create a second list, for
example if you need to use the result multiple times.
If your logic is too complicated for a short list comprehension or generator
expression, consider using a generator function instead of returning a list.
**Good**:
.. code-block:: python
def make_batches(items, batch_size):
"""
>>> list(make_batches([1, 2, 3, 4, 5], batch_size=3))
[[1, 2, 3], [4, 5]]
"""
current_batch = []
for item in items:
current_batch.append(item)
if len(current_batch) == batch_size:
yield current_batch
current_batch = []
yield current_batch
Never use a list comprehension just for its side effects.
**Bad**:
.. code-block:: python
[print(x) for x in seqeunce]
**Good**:
.. code-block:: python
for x in sequence:
print(x)
Filtering a list
~~~~~~~~~~~~~~~~
@@ -616,52 +677,15 @@ Don't make multiple passes through the list.
**Good**:
Python has a few standard ways of filtering lists.
The approach you use depends on:
* Python 2.x vs. 3.x
* Lists vs. iterators
* Possible side effects of modifying the original list
Python 2.x vs. 3.x
::::::::::::::::::
Starting with Python 3.0, the :py:func:`filter` function returns an iterator instead of a list.
Wrap it in :py:func:`list` if you truly need a list.
.. code-block:: python
list(filter(...))
List comprehensions and generator expressions work the same in both 2.x and 3.x (except that comprehensions in 2.x "leak" variables into the enclosing namespace):
* Comprehensions create a new list object.
* Generators iterate over the original list.
The :py:func:`filter` function:
* in 2.x returns a list (use itertools.ifilter if you want an iterator)
* in 3.x returns an iterator
Lists vs. iterators
:::::::::::::::::::
Creating a new list requires more work and uses more memory. If you a just going to loop through the new list, consider using an iterator instead.
Use a list comprehension or generator expression.
.. code-block:: python
# comprehensions create a new list object
filtered_values = [value for value in sequence if value != x]
# Or (2.x)
filtered_values = filter(lambda i: i != x, sequence)
# generators don't create another list
filtered_values = (value for value in sequence if value != x)
# Or (3.x)
filtered_values = filter(lambda i: i != x, sequence)
# Or (2.x)
filtered_values = itertools.ifilter(lambda i: i != x, sequence)
Possible side effects of modifying the original list
@@ -673,10 +697,6 @@ Modifying the original list can be risky if there are other variables referencin
# replace the contents of the original list
sequence[::] = [value for value in sequence if value != x]
# Or
sequence[::] = (value for value in sequence if value != x)
# Or
sequence[::] = filter(lambda value: value != x, sequence)
Modifying the values in a list
@@ -705,11 +725,6 @@ It's safer to create a new list object and leave the original alone.
# assign the variable "a" to a new list without changing "b"
a = [i + 3 for i in a]
# Or (Python 2.x):
a = map(lambda i: i + 3, a)
# Or (Python 3.x)
a = list(map(lambda i: i + 3, a))
Use :py:func:`enumerate` keep a count of your place in the list.