[python/en] Set, dict, and generator comprehensions (#2298)

* Add set and dict comprehensions for python 2 and 3

* Clean up formatting and generator explanation

* Include documentation for generator comprehensions
This commit is contained in:
J. Ryan Rembert 2016-07-03 01:47:57 -07:00 committed by ven
parent 5dee671bfe
commit e053806775
2 changed files with 56 additions and 32 deletions

View File

@ -8,21 +8,21 @@ contributors:
filename: learnpython.py filename: learnpython.py
--- ---
Python was created by Guido Van Rossum in the early 90s. It is now one of the Python was created by Guido Van Rossum in the early 90s. It is now one of the
most popular languages in existence. I fell in love with Python for its most popular languages in existence. I fell in love with Python for its
syntactic clarity. It's basically executable pseudocode. syntactic clarity. It's basically executable pseudocode.
Feedback would be highly appreciated! You can reach me at [@louiedinh](http://twitter.com/louiedinh) Feedback would be highly appreciated! You can reach me at [@louiedinh](http://twitter.com/louiedinh)
or louiedinh [at] [google's email service] or louiedinh [at] [google's email service]
Note: This article applies to Python 2.7 specifically, but should be applicable Note: This article applies to Python 2.7 specifically, but should be applicable
to Python 2.x. Python 2.7 is reaching end of life and will stop being to Python 2.x. Python 2.7 is reaching end of life and will stop being
maintained in 2020, it is though recommended to start learning Python with maintained in 2020, it is though recommended to start learning Python with
Python 3. For Python 3.x, take a look at the [Python 3 tutorial](http://learnxinyminutes.com/docs/python3/). Python 3. For Python 3.x, take a look at the [Python 3 tutorial](http://learnxinyminutes.com/docs/python3/).
It is also possible to write Python code which is compatible with Python 2.7 It is also possible to write Python code which is compatible with Python 2.7
and 3.x at the same time, using Python [`__future__` imports](https://docs.python.org/2/library/__future__.html). `__future__` imports and 3.x at the same time, using Python [`__future__` imports](https://docs.python.org/2/library/__future__.html). `__future__` imports
allow you to write Python 3 code that will run on Python 2, so check out the allow you to write Python 3 code that will run on Python 2, so check out the
Python 3 tutorial. Python 3 tutorial.
```python ```python
@ -549,6 +549,10 @@ filter(lambda x: x > 5, [3, 4, 5, 6, 7]) # => [6, 7]
[add_10(i) for i in [1, 2, 3]] # => [11, 12, 13] [add_10(i) for i in [1, 2, 3]] # => [11, 12, 13]
[x for x in [3, 4, 5, 6, 7] if x > 5] # => [6, 7] [x for x in [3, 4, 5, 6, 7] if x > 5] # => [6, 7]
# You can construct set and dict comprehensions as well.
{x for x in 'abcddeef' if x in 'abc'} # => {'d', 'e', 'f'}
{x: x**2 for x in range(5)} # => {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
#################################################### ####################################################
## 5. Classes ## 5. Classes
@ -668,10 +672,10 @@ import math
dir(math) dir(math)
# If you have a Python script named math.py in the same # If you have a Python script named math.py in the same
# folder as your current script, the file math.py will # folder as your current script, the file math.py will
# be loaded instead of the built-in Python module. # be loaded instead of the built-in Python module.
# This happens because the local folder has priority # This happens because the local folder has priority
# over Python's built-in libraries. # over Python's built-in libraries.
#################################################### ####################################################
@ -679,44 +683,54 @@ dir(math)
#################################################### ####################################################
# Generators # Generators
# A generator "generates" values as they are requested instead of storing # A generator "generates" values as they are requested instead of storing
# everything up front # everything up front
# The following method (*NOT* a generator) will double all values and store it # The following method (*NOT* a generator) will double all values and store it
# in `double_arr`. For large size of iterables, that might get huge! # in `double_arr`. For large size of iterables, that might get huge!
def double_numbers(iterable): def double_numbers(iterable):
double_arr = [] double_arr = []
for i in iterable: for i in iterable:
double_arr.append(i + i) double_arr.append(i + i)
# Running the following would mean we'll double all values first and return all # Running the following would mean we'll double all values first and return all
# of them back to be checked by our condition # of them back to be checked by our condition
for value in double_numbers(range(1000000)): # `test_non_generator` for value in double_numbers(range(1000000)): # `test_non_generator`
print value print value
if value > 5: if value > 5:
break break
# We could instead use a generator to "generate" the doubled value as the item # We could instead use a generator to "generate" the doubled value as the item
# is being requested # is being requested
def double_numbers_generator(iterable): def double_numbers_generator(iterable):
for i in iterable: for i in iterable:
yield i + i yield i + i
# Running the same code as before, but with a generator, now allows us to iterate # Running the same code as before, but with a generator, now allows us to iterate
# over the values and doubling them one by one as they are being consumed by # over the values and doubling them one by one as they are being consumed by
# our logic. Hence as soon as we see a value > 5, we stop break out of the # our logic. Hence as soon as we see a value > 5, we break out of the
# loop and don't need to double most of the values sent in (MUCH FASTER!) # loop and don't need to double most of the values sent in (MUCH FASTER!)
for value in double_numbers_generator(xrange(1000000)): # `test_generator` for value in double_numbers_generator(xrange(1000000)): # `test_generator`
print value print value
if value > 5: if value > 5:
break break
# BTW: did you notice the use of `range` in `test_non_generator` and `xrange` in `test_generator`? # BTW: did you notice the use of `range` in `test_non_generator` and `xrange` in `test_generator`?
# Just as `double_numbers_generator` is the generator version of `double_numbers` # Just as `double_numbers_generator` is the generator version of `double_numbers`
# We have `xrange` as the generator version of `range` # We have `xrange` as the generator version of `range`
# `range` would return back and array with 1000000 values for us to use # `range` would return back and array with 1000000 values for us to use
# `xrange` would generate 1000000 values for us as we request / iterate over those items # `xrange` would generate 1000000 values for us as we request / iterate over those items
# Just as you can create a list comprehension, you can create generator
# comprehensions as well.
values = (-x for x in [1,2,3,4,5])
for x in values:
print(x) # prints -1 -2 -3 -4 -5 to console/terminal
# You can also cast a generator comprehension directly to a list.
values = (-x for x in [1,2,3,4,5])
gen_to_list = list(values)
print(gen_to_list) # => [-1, -2, -3, -4, -5]
# Decorators # Decorators

View File

@ -601,6 +601,10 @@ list(filter(lambda x: x > 5, [3, 4, 5, 6, 7])) # => [6, 7]
[add_10(i) for i in [1, 2, 3]] # => [11, 12, 13] [add_10(i) for i in [1, 2, 3]] # => [11, 12, 13]
[x for x in [3, 4, 5, 6, 7] if x > 5] # => [6, 7] [x for x in [3, 4, 5, 6, 7] if x > 5] # => [6, 7]
# You can construct set and dict comprehensions as well.
{x for x in 'abcddeef' if x in 'abc'} # => {'d', 'e', 'f'}
{x: x**2 for x in range(5)} # => {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
#################################################### ####################################################
## 5. Modules ## 5. Modules
@ -816,7 +820,7 @@ if __name__ == '__main__':
sup.age = 100 sup.age = 100
print(sup.age) print(sup.age)
# Inherited attribute from 2nd ancestor whose default value was overriden # Inherited attribute from 2nd ancestor whose default value was overridden.
print('Can I fly? ' + str(sup.fly)) print('Can I fly? ' + str(sup.fly))
@ -825,29 +829,35 @@ if __name__ == '__main__':
## 7. Advanced ## 7. Advanced
#################################################### ####################################################
# Generators help you make lazy code # Generators help you make lazy code.
def double_numbers(iterable): def double_numbers(iterable):
for i in iterable: for i in iterable:
yield i + i yield i + i
# A generator creates values on the fly. # Generators are memory-efficient because they only load the data needed to
# Instead of generating and returning all values at once it creates one in each # process the next value in the iterable. This allows them to perform
# iteration. This means values bigger than 15 wont be processed in # operations on otherwise prohibitively large value ranges.
# double_numbers. # NOTE: `range` replaces `xrange` in Python 3.
# We use a trailing underscore in variable names when we want to use a name that for i in double_numbers(range(1, 900000000)): # `range` is a generator.
# would normally collide with a python keyword
range_ = range(1, 900000000)
# will double all numbers until a result >=30 found
for i in double_numbers(range_):
print(i) print(i)
if i >= 30: if i >= 30:
break break
# Just as you can create a list comprehension, you can create generator
# comprehensions as well.
values = (-x for x in [1,2,3,4,5])
for x in values:
print(x) # prints -1 -2 -3 -4 -5 to console/terminal
# You can also cast a generator comprehension directly to a list.
values = (-x for x in [1,2,3,4,5])
gen_to_list = list(values)
print(gen_to_list) # => [-1, -2, -3, -4, -5]
# Decorators # Decorators
# in this example beg wraps say # In this example `beg` wraps `say`. If say_please is True then it
# Beg will call say. If say_please is True then it will change the returned # will change the returned message.
# message
from functools import wraps from functools import wraps