Day 1: Notable Features of Python

Functions vs. Generators

  • Generators let you model problems/functions differently.
  • Generators are often used when you need to iterate through some computed values

In [21]:
def gen(x):
    yield x+1
    yield x+2
for x in gen(11):
    print(x)


12
13

Two things we do in python:

  • call functions
  • iterate over collections

Generator expressions look like tuple comprehensions


In [28]:
gener1 = (x for x in range(4))
print(gener1)
for x in gener1:
    print(x)

def gener2(length):
    for x in range(length):
        yield x
print(gener2)
for x in gener2(4):
    print(x)


<generator object <genexpr> at 0x7f603382d820>
0
1
2
3
<function gener2 at 0x7f6033851c20>
0
1
2
3

An Iterator is merely an iterable and state that remembers where we were.

You can think of a generator as a delayed computation; you only get results as you ask for them. Infinite lists!

Calling a generator will run it until the first yield, then it pauses until it's called again.

gi.next() == gi.send(None)

Generators are generally used for efficiency purposes

Context Managers

Context Managers are a good wrapper around opening files

  • allows you to refactor almost all gated code; handles setup and tear down
  • better semantics: makes it clear that the resource is only used in the with-block
  • automatic cleanup

don't:

f = open('file.txt')
lines = [line for line in f.readline()]
f.close()
print(lines)

do:

with open('file.txt') as f:
    lines = [line for line in f.readline()]
print(lines)

watch out for resource leakage, though

Use sorted() instead of [].sort()

namedtuple has the same memory footprint as the equivalent tuple

you can do attribute lookup in string formatting:


In [ ]:
class Foo(object):
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __repr__(self):
        return 'Foo({0.x}, {0.y})'.format(self)

defaultdict


In [17]:
from collections import defaultdict

x = defaultdict(int)  # calling int() gives you 0

print("int() == {}".format(int()))
x['a'] = 1


int() == 0

In [18]:
print(x)


defaultdict(<class 'int'>, {'a': 1})

In [19]:
print("x['a'] == {}".format(x['a']))
print("x['b'] == {}".format(x['b']))


x['a'] == 1
x['b'] == 0

In [20]:
default = lambda: None

y = defaultdict(default)
print(y['one'])
y['one'] = 'hello'
print(y['one'])
print(y)


None
hello
defaultdict(<function <lambda> at 0x7f603383d290>, {'one': 'hello'})