squares.py
def square1(n):
return n ** 2 # squaring through the power operator
def square2(n):
return n * n # squaring through multiplication
map, zip, and filter
map
map(function, iterable, ...) returns an iterator that applies function to every item of iterable, yielding the results. If additional iterable arguments are passed, function must take that many arguments and is applied to the items from all iterables in parallel. With multiple iterables, the iterator stops when the shortest iterable is exhausted.
map.example.py
map(lambda *a: a, range(3)) # without wrapping in list...
list(map(lambda *a: a, range(3))) # wrapping in list...
list(map(lambda *a: a, range(3), 'abc')) # 2 iterables
list(map(lambda *a: a, range(3), 'abc', range(4, 7))) # 3
# map stops at the shortest iterator
list(map(lambda *a: a, (), 'abc')) # empty tuple is shortest
list(map(lambda *a: a, (1, 2), 'abc')) # (1, 2) shortest
list(map(lambda *a: a, (1, 2, 3, 4), 'abc')) # 'abc' shortest
decorate.sort.undecorate.py
students = [
dict(id=0, credits=dict(math=9, physics=6, history=7)),
dict(id=1, credits=dict(math=6, physics=7, latin=10)),
dict(id=2, credits=dict(history=8, physics=9, chemistry=10)),
dict(id=3, credits=dict(math=5, physics=5, geography=7)),
]
def decorate(student):
# create a 2-tuple (sum of credits, student) from student dict
return (sum(student['credits'].values()), student)
def undecorate(decorated_student):
# discard sum of credits, return original student dict
return decorated_student[1]
students = sorted(map(decorate, students), reverse=True)
students = list(map(undecorate, students))
$ python decorate.sort.undecorate.py
zip
zip(*iterables) returns an iterator of tuples, where the i-th tuple contains the i-th element from each of the argument sequences or iterables. The iterator stops when the shortest input iterable is exhausted. With a single iterable argument, it returns an iterator of 1-tuples. With no arguments, it returns an empty iterator.
zip.grades.py
grades = [18, 23, 30, 27, 15, 9, 22]
avgs = [22, 21, 29, 24, 18, 18, 24]
list(zip(avgs, grades))
list(map(lambda *a: a, avgs, grades)) # equivalent to zip
maxims.py
a = [5, 9, 2, 4, 7]
b = [3, 7, 1, 9, 2]
c = [6, 8, 0, 5, 3]
maxs = map(lambda n: max(*n), zip(a, b, c))
list(maxs)
filter
filter(function, iterable) construct an iterator from those elements of iterable for which function returns True. iterable may be either a sequence, a container which supports iteration, or an iterator. If function is None, the identity function is assumed, that is, all elements of iterable that are false are removed.
filter.py
test = [2, 5, 8, 0, 0, 1, 0]
list(filter(None, test))
list(filter(lambda x: x, test)) # equivalent to previous one
list(filter(lambda x: x > 4, test)) # keep only items > 4
-- Comprehensions
squares.map.py
# If you code like this you are not a Python guy! ;)
squares = []
for n in range(10):
squares.append(n ** 2)
list(squares)
# This is better, one line, nice and readable
squares = map(lambda n: n**2, range(10))
list(squares)
squares.comprehension.py
[n ** 2 for n in range(10)]
even.squares.py
# using map and filter
sq1 = list(
filter(lambda n: not n % 2, map(lambda n: n ** 2, range(10)))
)
# equivalent, but using list comprehensions
sq2 = [n ** 2 for n in range(10) if not n % 2]
print(sq1, sq1 == sq2) # prints: [0, 4, 16, 36, 64] True
A list comprehension consists of brackets containing an expression followed by a for clause, then zero or more for or if clauses. The result will be a new list resulting from evaluating the expression in the context of the for and if clauses which follow it.
Nested comprehensions
pairs.for.loop.py
items = 'ABCDE'
pairs = []
for a in range(len(items)):
for b in range(a, len(items)):
pairs.append((items[a], items[b]))
pairs.list.comprehension.py
items = 'ABCDE'
pairs = [(items[a], items[b])
for a in range(len(items)) for b in range(a, len(items))]
Filtering a comprehension
pythagorean.triple.py
from math import sqrt
# this will generate all possible pairs
mx = 10
legs = [(a, b, sqrt(a**2 + b**2))
for a in range(1, mx) for b in range(a, mx)]
# this will filter out all non pythagorean triples
legs = list(
filter(lambda triple: triple[2].is_integer(), legs))
print(legs) # prints: [(3, 4, 5.0), (6, 8, 10.0)]
pythagorean.triple.int.py
from math import sqrt
mx = 10
legs = [(a, b, sqrt(a**2 + b**2))
for a in range(1, mx) for b in range(a, mx)]
legs = filter(lambda triple: triple[2].is_integer(), legs)
# this will make the third number in the tuples integer
legs = list(
map(lambda triple: triple[:2] + (int(triple[2]), ), legs))
print(legs) # prints: [(3, 4, 5), (6, 8, 10)]
pythagorean.triple.comprehension.py
from math import sqrt
# this step is the same as before
mx = 10
legs = [(a, b, sqrt(a**2 + b**2))
for a in range(1, mx) for b in range(a, mx)]
# here we combine filter and map in one CLEAN list comprehension
legs = [(a, b, int(c)) for a, b, c in legs if c.is_integer()]
print(legs) # prints: [(3, 4, 5), (6, 8, 10)]
dict comprehensions
dictionary.comprehensions.py
from string import ascii_lowercase
lettermap = dict((c, k) for k, c in enumerate(ascii_lowercase, 1))
lettermap = {c: k for k, c in enumerate(ascii_lowercase, 1)}
dictionary.comprehensions.duplicates.py
word = 'Hello'
swaps = {c: c.swapcase() for c in word}
print(swaps) # prints: {'o': 'O', 'l': 'L', 'e': 'E', 'H': 'h'}
dictionary.comprehensions.positions.py
word = 'Hello'
positions = {c: k for k, c in enumerate(word)}
print(positions) # prints: {'l': 3, 'o': 4, 'e': 1, 'H': 0}
set comprehensions
set.comprehensions.py
word = 'Hello'
letters1 = set(c for c in word)
letters2 = {c for c in word}
print(letters1) # prints: {'l', 'o', 'H', 'e'}
print(letters1 == letters2) # prints: True
-- Generators
Generator functions
first.n.squares.py
def get_squares(n): # classic function approach
return [x ** 2 for x in range(n)]
print(get_squares(10))
def get_squares_gen(n): # generator approach
for x in range(n):
yield x ** 2 # we yield, we don't return
print(list(get_squares_gen(10)))
first.n.squares.manual.py
def get_squares_gen(n):
for x in range(n):
yield x ** 2
squares = get_squares_gen(4) # this creates a generator object
print(squares) # <generator object get_squares_gen at 0x7f158...>
print(next(squares)) # prints: 0
print(next(squares)) # prints: 1
print(next(squares)) # prints: 4
print(next(squares)) # prints: 9
# the following raises StopIteration, the generator is exhausted,
# any further call to next will keep raising StopIteration
print(next(squares))
gen.yield.return.py
def geometric_progression(a, q):
k = 0
while True:
result = a * q**k
if result <= 100000:
yield result
else:
return
k += 1
for n in geometric_progression(2, 5):
print(n)
Going beyond next
first.n.squares.manual.method.py
def get_squares_gen(n):
for x in range(n):
yield x ** 2
squares = get_squares_gen(3)
print(squares.__next__()) # prints: 0
print(squares.__next__()) # prints: 1
print(squares.__next__()) # prints: 4
# the following raises StopIteration, the generator is exhausted,
# any further call to next will keep raising StopIteration
print(squares.__next__())
gen.send.preparation.py
def counter(start=0):
n = start
while True:
yield n
n += 1
c = counter()
print(next(c)) # prints: 0
print(next(c)) # prints: 1
print(next(c)) # prints: 2
gen.send.preparation.stop.py
stop = False
def counter(start=0):
n = start
while not stop:
yield n
n += 1
c = counter()
print(next(c)) # prints: 0
print(next(c)) # prints: 1
stop = True
print(next(c)) # raises StopIteration
gen.send.py
def counter(start=0):
n = start
while True:
result = yield n # A
print(type(result), result) # B
if result == 'Q':
break
n += 1
c = counter()
print(next(c)) # C
print(c.send('Wow!')) # D
print(next(c)) # E
print(c.send('Q')) # F
$ python gen.send.py
The yield from expression
gen.yield.for.py
def print_squares(start, end):
for n in range(start, end):
yield n ** 2
for n in print_squares(2, 5):
print(n)
gen.yield.from.py
def print_squares(start, end):
yield from (n ** 2 for n in range(start, end))
for n in print_squares(2, 5):
print(n)
Generator expressions
generator.expressions.py
cubes = [k**3 for k in range(10)] # regular list
cubes
type(cubes)
cubes_gen = (k**3 for k in range(10)) # create as generator
cubes_gen
type(cubes_gen)
list(cubes_gen) # this will exhaust the generator
list(cubes_gen) # nothing more to give
gen.map.py
def adder(*n):
return sum(n)
s1 = sum(map(lambda n: adder(*n), zip(range(100), range(1, 101))))
s2 = sum(adder(*n) for n in zip(range(100), range(1, 101)))
gen.filter.py
cubes = [x**3 for x in range(10)]
odd_cubes1 = filter(lambda cube: cube % 2, cubes)
odd_cubes2 = (cube for cube in cubes if cube % 2)
gen.map.filter.py
N = 20
cubes1 = map(
lambda n: (n, n**3),
filter(lambda n: n % 3 == 0 or n % 5 == 0, range(N))
)
cubes2 = (
(n, n**3) for n in range(N) if n % 3 == 0 or n % 5 == 0)
sum.example.py
s1 = sum([n**2 for n in range(10**6)])
s2 = sum((n**2 for n in range(10**6)))
s3 = sum(n**2 for n in range(10**6))
sum.example.2.py
s = sum([n**2 for n in range(10**8)]) # this is killed
# s = sum(n**2 for n in range(10**8)) # this succeeds
print(s)
$ python sum.example.2.py
After comment out the first line, and uncomment the second one, this is the result:
$ python sum.example.2.py
-- Some performnace considerations
performances.py
from time import time
mx = 5500 # this is the max I could reach with my computer...
t = time() # start time for the for loop
dmloop = []
for a in range(1, mx):
for b in range(a, mx):
dmloop.append(divmod(a, b))
print('for loop: {:.4f} s'.format(time() - t)) # elapsed time
t = time() # start time for the list comprehension
dmlist = [
divmod(a, b) for a in range(1, mx) for b in range(a, mx)]
print('list comprehension: {:.4f} s'.format(time() - t))
t = time() # start time for the generator expression
dmgen = list(
divmod(a, b) for a in range(1, mx) for b in range(a, mx))
print('generator expression: {:.4f} s'.format(time() - t))
# verify correctness of results and number of items in each list
print(dmloop == dmlist == dmgen, len(dmloop))
$ python performances.py
performances.map.py
from time import time
mx = 2 * 10 ** 7
t = time()
absloop = []
for n in range(mx):
absloop.append(abs(n))
print('for loop: {:.4f} s'.format(time() - t))
t = time()
abslist = [abs(n) for n in range(mx)]
print('list comprehension: {:.4f} s'.format(time() - t))
t = time()
absmap = list(map(abs, range(mx)))
print('map: {:.4f} s'.format(time() - t))
print(absloop == abslist == absmap)
$ python performances.map.py
-- Don't overdo comprehensions and generators
import this
functions.py
def gcd(a, b):
"""Calculate the Greatest Common Divisor of (a, b). """
while b != 0:
a, b = b, a % b
return a
pythagorean.triple.generation.py
from functions import gcd
N = 50
triples = sorted( # 1
((a, b, c) for a, b, c in ( # 2
((m**2 - n**2), (2 * m * n), (m**2 + n**2)) # 3
for m in range(1, int(N**.5) + 1) # 4
for n in range(1, m) # 5
if (m - n) % 2 and gcd(m, n) == 1 # 6
) if c <= N), key=lambda *triple: sum(*triple) # 7
)
print(triples)
pythagorean.triple.generation.for.py
from functions import gcd
def gen_triples(N):
for m in range(1, int(N**.5) + 1): # 1
for n in range(1, m): # 2
if (m - n) % 2 and gcd(m, n) == 1: # 3
c = m**2 + n**2 # 4
if c <= N: # 5
a = m**2 - n**2 # 6
b = 2 * m * n # 7
yield (a, b, c) # 8
triples = sorted(
gen_triples(50), key=lambda *triple: sum(*triple)) # 9
print(triples)
$ python pythagorean.triple.generation.py
-- Name localization
scopes.py
A = 100
ex1 = [A for A in range(5)]
print(A) # prints: 100
ex2 = list(A for A in range(5))
print(A) # prints: 100
ex3 = dict((A, 2 * A) for A in range(5))
print(A) # prints: 100
ex4 = set(A for A in range(5))
print(A) # prints: 100
s = 0
for A in range(5):
s += A
print(A) # prints: 4
scopes.noglobal.py
ex1 = [A for A in range(5)]
print(A) # breaks: NameError: name 'A' is not defined
scopes.for.py
s = 0
for A in range(5):
s += A
print(A) # prints: 4
print(globals())
$ python scopes.for.py
-- Generation behavior in built-ins
-- One last example
fibonacci.first.py
def fibonacci(N):
"""Return all fibonacci numbers up to N. """
result = [0]
next_n = 1
while next_n <= N:
result.append(next_n)
next_n = sum(result[-2:])
return result
print(fibonacci(0)) # [0]
print(fibonacci(1)) # [0, 1, 1]
print(fibonacci(50)) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
fibonacci.second.py
def fibonacci(N):
"""Return all fibonacci numbers up to N. """
yield 0
if N == 0:
return
a = 0
b = 1
while b <= N:
yield b
a, b = b, a + b
print(list(fibonacci(0))) # [0]
print(list(fibonacci(1))) # [0, 1, 1]
print(list(fibonacci(50))) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
fibonacci.elegant.py
def fibonacci(N):
"""Return all fibonacci numbers up to N. """
a, b = 0, 1
while a <= N:
yield a
a, b = b, a + b
Chap5 Saving Time and Memory
squares.py
def square1(n):
return n ** 2 # squaring through the power operator
def square2(n):
return n * n # squaring through multiplication
map, zip, and filter
map
map(function, iterable, ...) returns an iterator that applies function to every item of iterable, yielding the results. If additional iterable arguments are passed, function must take that many arguments and is applied to the items from all iterables in parallel. With multiple iterables, the iterator stops when the shortest iterable is exhausted.
map.example.py
map(lambda *a: a, range(3)) # without wrapping in list...
list(map(lambda *a: a, range(3))) # wrapping in list...
list(map(lambda *a: a, range(3), 'abc')) # 2 iterables
list(map(lambda *a: a, range(3), 'abc', range(4, 7))) # 3
# map stops at the shortest iterator
list(map(lambda *a: a, (), 'abc')) # empty tuple is shortest
list(map(lambda *a: a, (1, 2), 'abc')) # (1, 2) shortest
list(map(lambda *a: a, (1, 2, 3, 4), 'abc')) # 'abc' shortest
decorate.sort.undecorate.py
students = [
dict(id=0, credits=dict(math=9, physics=6, history=7)),
dict(id=1, credits=dict(math=6, physics=7, latin=10)),
dict(id=2, credits=dict(history=8, physics=9, chemistry=10)),
dict(id=3, credits=dict(math=5, physics=5, geography=7)),
]
def decorate(student):
# create a 2-tuple (sum of credits, student) from student dict
return (sum(student['credits'].values()), student)
def undecorate(decorated_student):
# discard sum of credits, return original student dict
return decorated_student[1]
students = sorted(map(decorate, students), reverse=True)
students = list(map(undecorate, students))
$ python decorate.sort.undecorate.py
zip
zip(*iterables) returns an iterator of tuples, where the i-th tuple contains the i-th element from each of the argument sequences or iterables. The iterator stops when the shortest input iterable is exhausted. With a single iterable argument, it returns an iterator of 1-tuples. With no arguments, it returns an empty iterator.
zip.grades.py
grades = [18, 23, 30, 27, 15, 9, 22]
avgs = [22, 21, 29, 24, 18, 18, 24]
list(zip(avgs, grades))
list(map(lambda *a: a, avgs, grades)) # equivalent to zip
maxims.py
a = [5, 9, 2, 4, 7]
b = [3, 7, 1, 9, 2]
c = [6, 8, 0, 5, 3]
maxs = map(lambda n: max(*n), zip(a, b, c))
list(maxs)
filter
filter(function, iterable) construct an iterator from those elements of iterable for which function returns True. iterable may be either a sequence, a container which supports iteration, or an iterator. If function is None, the identity function is assumed, that is, all elements of iterable that are false are removed.
filter.py
test = [2, 5, 8, 0, 0, 1, 0]
list(filter(None, test))
list(filter(lambda x: x, test)) # equivalent to previous one
list(filter(lambda x: x > 4, test)) # keep only items > 4
-- Comprehensions
squares.map.py
# If you code like this you are not a Python guy! ;)
squares = []
for n in range(10):
squares.append(n ** 2)
list(squares)
# This is better, one line, nice and readable
squares = map(lambda n: n**2, range(10))
list(squares)
squares.comprehension.py
[n ** 2 for n in range(10)]
even.squares.py
# using map and filter
sq1 = list(
filter(lambda n: not n % 2, map(lambda n: n ** 2, range(10)))
)
# equivalent, but using list comprehensions
sq2 = [n ** 2 for n in range(10) if not n % 2]
print(sq1, sq1 == sq2) # prints: [0, 4, 16, 36, 64] True
A list comprehension consists of brackets containing an expression followed by a for clause, then zero or more for or if clauses. The result will be a new list resulting from evaluating the expression in the context of the for and if clauses which follow it.
Nested comprehensions
pairs.for.loop.py
items = 'ABCDE'
pairs = []
for a in range(len(items)):
for b in range(a, len(items)):
pairs.append((items[a], items[b]))
pairs.list.comprehension.py
items = 'ABCDE'
pairs = [(items[a], items[b])
for a in range(len(items)) for b in range(a, len(items))]
Filtering a comprehension
pythagorean.triple.py
from math import sqrt
# this will generate all possible pairs
mx = 10
legs = [(a, b, sqrt(a**2 + b**2))
for a in range(1, mx) for b in range(a, mx)]
# this will filter out all non pythagorean triples
legs = list(
filter(lambda triple: triple[2].is_integer(), legs))
print(legs) # prints: [(3, 4, 5.0), (6, 8, 10.0)]
pythagorean.triple.int.py
from math import sqrt
mx = 10
legs = [(a, b, sqrt(a**2 + b**2))
for a in range(1, mx) for b in range(a, mx)]
legs = filter(lambda triple: triple[2].is_integer(), legs)
# this will make the third number in the tuples integer
legs = list(
map(lambda triple: triple[:2] + (int(triple[2]), ), legs))
print(legs) # prints: [(3, 4, 5), (6, 8, 10)]
pythagorean.triple.comprehension.py
from math import sqrt
# this step is the same as before
mx = 10
legs = [(a, b, sqrt(a**2 + b**2))
for a in range(1, mx) for b in range(a, mx)]
# here we combine filter and map in one CLEAN list comprehension
legs = [(a, b, int(c)) for a, b, c in legs if c.is_integer()]
print(legs) # prints: [(3, 4, 5), (6, 8, 10)]
dict comprehensions
dictionary.comprehensions.py
from string import ascii_lowercase
lettermap = dict((c, k) for k, c in enumerate(ascii_lowercase, 1))
lettermap = {c: k for k, c in enumerate(ascii_lowercase, 1)}
dictionary.comprehensions.duplicates.py
word = 'Hello'
swaps = {c: c.swapcase() for c in word}
print(swaps) # prints: {'o': 'O', 'l': 'L', 'e': 'E', 'H': 'h'}
dictionary.comprehensions.positions.py
word = 'Hello'
positions = {c: k for k, c in enumerate(word)}
print(positions) # prints: {'l': 3, 'o': 4, 'e': 1, 'H': 0}
set comprehensions
set.comprehensions.py
word = 'Hello'
letters1 = set(c for c in word)
letters2 = {c for c in word}
print(letters1) # prints: {'l', 'o', 'H', 'e'}
print(letters1 == letters2) # prints: True
-- Generators
Generator functions
first.n.squares.py
def get_squares(n): # classic function approach
return [x ** 2 for x in range(n)]
print(get_squares(10))
def get_squares_gen(n): # generator approach
for x in range(n):
yield x ** 2 # we yield, we don't return
print(list(get_squares_gen(10)))
first.n.squares.manual.py
def get_squares_gen(n):
for x in range(n):
yield x ** 2
squares = get_squares_gen(4) # this creates a generator object
print(squares) # <generator object get_squares_gen at 0x7f158...>
print(next(squares)) # prints: 0
print(next(squares)) # prints: 1
print(next(squares)) # prints: 4
print(next(squares)) # prints: 9
# the following raises StopIteration, the generator is exhausted,
# any further call to next will keep raising StopIteration
print(next(squares))
gen.yield.return.py
def geometric_progression(a, q):
k = 0
while True:
result = a * q**k
if result <= 100000:
yield result
else:
return
k += 1
for n in geometric_progression(2, 5):
print(n)
Going beyond next
first.n.squares.manual.method.py
def get_squares_gen(n):
for x in range(n):
yield x ** 2
squares = get_squares_gen(3)
print(squares.__next__()) # prints: 0
print(squares.__next__()) # prints: 1
print(squares.__next__()) # prints: 4
# the following raises StopIteration, the generator is exhausted,
# any further call to next will keep raising StopIteration
print(squares.__next__())
gen.send.preparation.py
def counter(start=0):
n = start
while True:
yield n
n += 1
c = counter()
print(next(c)) # prints: 0
print(next(c)) # prints: 1
print(next(c)) # prints: 2
gen.send.preparation.stop.py
stop = False
def counter(start=0):
n = start
while not stop:
yield n
n += 1
c = counter()
print(next(c)) # prints: 0
print(next(c)) # prints: 1
stop = True
print(next(c)) # raises StopIteration
gen.send.py
def counter(start=0):
n = start
while True:
result = yield n # A
print(type(result), result) # B
if result == 'Q':
break
n += 1
c = counter()
print(next(c)) # C
print(c.send('Wow!')) # D
print(next(c)) # E
print(c.send('Q')) # F
$ python gen.send.py
The yield from expression
gen.yield.for.py
def print_squares(start, end):
for n in range(start, end):
yield n ** 2
for n in print_squares(2, 5):
print(n)
gen.yield.from.py
def print_squares(start, end):
yield from (n ** 2 for n in range(start, end))
for n in print_squares(2, 5):
print(n)
Generator expressions
generator.expressions.py
cubes = [k**3 for k in range(10)] # regular list
cubes
type(cubes)
cubes_gen = (k**3 for k in range(10)) # create as generator
cubes_gen
type(cubes_gen)
list(cubes_gen) # this will exhaust the generator
list(cubes_gen) # nothing more to give
gen.map.py
def adder(*n):
return sum(n)
s1 = sum(map(lambda n: adder(*n), zip(range(100), range(1, 101))))
s2 = sum(adder(*n) for n in zip(range(100), range(1, 101)))
gen.filter.py
cubes = [x**3 for x in range(10)]
odd_cubes1 = filter(lambda cube: cube % 2, cubes)
odd_cubes2 = (cube for cube in cubes if cube % 2)
gen.map.filter.py
N = 20
cubes1 = map(
lambda n: (n, n**3),
filter(lambda n: n % 3 == 0 or n % 5 == 0, range(N))
)
cubes2 = (
(n, n**3) for n in range(N) if n % 3 == 0 or n % 5 == 0)
sum.example.py
s1 = sum([n**2 for n in range(10**6)])
s2 = sum((n**2 for n in range(10**6)))
s3 = sum(n**2 for n in range(10**6))
sum.example.2.py
s = sum([n**2 for n in range(10**8)]) # this is killed
# s = sum(n**2 for n in range(10**8)) # this succeeds
print(s)
$ python sum.example.2.py
After comment out the first line, and uncomment the second one, this is the result:
$ python sum.example.2.py
-- Some performnace considerations
performances.py
from time import time
mx = 5500 # this is the max I could reach with my computer...
t = time() # start time for the for loop
dmloop = []
for a in range(1, mx):
for b in range(a, mx):
dmloop.append(divmod(a, b))
print('for loop: {:.4f} s'.format(time() - t)) # elapsed time
t = time() # start time for the list comprehension
dmlist = [
divmod(a, b) for a in range(1, mx) for b in range(a, mx)]
print('list comprehension: {:.4f} s'.format(time() - t))
t = time() # start time for the generator expression
dmgen = list(
divmod(a, b) for a in range(1, mx) for b in range(a, mx))
print('generator expression: {:.4f} s'.format(time() - t))
# verify correctness of results and number of items in each list
print(dmloop == dmlist == dmgen, len(dmloop))
$ python performances.py
performances.map.py
from time import time
mx = 2 * 10 ** 7
t = time()
absloop = []
for n in range(mx):
absloop.append(abs(n))
print('for loop: {:.4f} s'.format(time() - t))
t = time()
abslist = [abs(n) for n in range(mx)]
print('list comprehension: {:.4f} s'.format(time() - t))
t = time()
absmap = list(map(abs, range(mx)))
print('map: {:.4f} s'.format(time() - t))
print(absloop == abslist == absmap)
$ python performances.map.py
-- Don't overdo comprehensions and generators
import this
functions.py
def gcd(a, b):
"""Calculate the Greatest Common Divisor of (a, b). """
while b != 0:
a, b = b, a % b
return a
pythagorean.triple.generation.py
from functions import gcd
N = 50
triples = sorted( # 1
((a, b, c) for a, b, c in ( # 2
((m**2 - n**2), (2 * m * n), (m**2 + n**2)) # 3
for m in range(1, int(N**.5) + 1) # 4
for n in range(1, m) # 5
if (m - n) % 2 and gcd(m, n) == 1 # 6
) if c <= N), key=lambda *triple: sum(*triple) # 7
)
print(triples)
pythagorean.triple.generation.for.py
from functions import gcd
def gen_triples(N):
for m in range(1, int(N**.5) + 1): # 1
for n in range(1, m): # 2
if (m - n) % 2 and gcd(m, n) == 1: # 3
c = m**2 + n**2 # 4
if c <= N: # 5
a = m**2 - n**2 # 6
b = 2 * m * n # 7
yield (a, b, c) # 8
triples = sorted(
gen_triples(50), key=lambda *triple: sum(*triple)) # 9
print(triples)
$ python pythagorean.triple.generation.py
-- Name localization
scopes.py
A = 100
ex1 = [A for A in range(5)]
print(A) # prints: 100
ex2 = list(A for A in range(5))
print(A) # prints: 100
ex3 = dict((A, 2 * A) for A in range(5))
print(A) # prints: 100
ex4 = set(A for A in range(5))
print(A) # prints: 100
s = 0
for A in range(5):
s += A
print(A) # prints: 4
scopes.noglobal.py
ex1 = [A for A in range(5)]
print(A) # breaks: NameError: name 'A' is not defined
scopes.for.py
s = 0
for A in range(5):
s += A
print(A) # prints: 4
print(globals())
$ python scopes.for.py
-- Generation behavior in built-ins
-- One last example
fibonacci.first.py
def fibonacci(N):
"""Return all fibonacci numbers up to N. """
result = [0]
next_n = 1
while next_n <= N:
result.append(next_n)
next_n = sum(result[-2:])
return result
print(fibonacci(0)) # [0]
print(fibonacci(1)) # [0, 1, 1]
print(fibonacci(50)) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
fibonacci.second.py
def fibonacci(N):
"""Return all fibonacci numbers up to N. """
yield 0
if N == 0:
return
a = 0
b = 1
while b <= N:
yield b
a, b = b, a + b
print(list(fibonacci(0))) # [0]
print(list(fibonacci(1))) # [0, 1, 1]
print(list(fibonacci(50))) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
fibonacci.elegant.py
def fibonacci(N):
"""Return all fibonacci numbers up to N. """
a, b = 0, 1
while a <= N:
yield a
a, b = b, a + b
No comments:
Post a Comment