Merge pull request #971 from gbrova/gbrova/prefer-list-comps-over-map

Prefer list comprehensions over map and filter
This commit is contained in:
Marc Poulin
2019-01-15 09:44:54 -07:00
committed by GitHub
2 changed files with 74 additions and 71 deletions
+8 -20
View File
@@ -809,16 +809,12 @@ and can be used as a key for a dictionary.
One peculiarity of Python that can surprise beginners is that
strings are immutable. This means that when constructing a string from
its parts, it is much more efficient to accumulate the parts in a list,
which is mutable, and then glue ('join') the parts together when the
full string is needed. One thing to notice, however, is that list
comprehensions are better and faster than constructing a list in a loop
with calls to ``append()``.
One other option is using the map function, which can 'map' a function
('str') to an iterable ('range(20)'). This results in a map object,
which you can then ('join') together just like the other examples.
The map function can be even faster than a list comprehension in some cases.
its parts, appending each part to the string is inefficient because
the entirety of the string is copied on each append.
Instead, it is much more efficient to accumulate the parts in a list,
which is mutable, and then glue (``join``) the parts together when the
full string is needed. List comprehensions are usually the fastest and
most idiomatic way to do this.
**Bad**
@@ -830,7 +826,7 @@ The map function can be even faster than a list comprehension in some cases.
nums += str(n) # slow and inefficient
print nums
**Good**
**Better**
.. code-block:: python
@@ -840,20 +836,12 @@ The map function can be even faster than a list comprehension in some cases.
nums.append(str(n))
print "".join(nums) # much more efficient
**Better**
.. code-block:: python
# create a concatenated string from 0 to 19 (e.g. "012..1819")
nums = [str(n) for n in range(20)]
print "".join(nums)
**Best**
.. code-block:: python
# create a concatenated string from 0 to 19 (e.g. "012..1819")
nums = map(str, range(20))
nums = [str(n) for n in range(20)]
print "".join(nums)
One final thing to mention about strings is that using ``join()`` is not always
+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.