Tips on Python
What are generators, and what are they for?
A generator is in some ways like an object with anext()
function.
Here are two ways to generate a sequence of Fibonnaci numbers.
class FibGenerator(): def __init__(self): self.a = 0 self.b = 1 def next(self): res = self.b self.a,self.b = self.b,self.a+self.b return res def fib(): a,b = 0,1 while True: yield b a,b = b,a+b g1 = FibGenerator() g2 = fib() for i in range(10): print g1.next(), g2.next()Under the hood, Python sees that the
fib()
function
contains the yield
keyword, so it realizes that
fib()
is a generator, so it gives it a next()
method. Each time you call g2.next()
, Python
re-enters the fib()
function and keeps on executing until it
hits yield
, whereupon it yields up that value. Next time you call g2.next()
the generator will start executing again immediately after the yield statement.
Why bother with generators?
Why bother with generators when the same thing could just as well be done with an object? Here is an example where generators are more elegant. I want to generate a sequence (U1, 1-U1, U2, 1-U2,…) where the Ui are independent random variables.class AlternatingGenerator(): def __init__(self): self.lastx = None def next(self): if self.lastx is None: self.lastx = random.random() res = self.lastx else: res = 1-self.lastx self.lastx = None return res def ralternating(): while True: x = random.random() yield x yield 1-xThe object-based solution requires you to turn your program logic inside-out. The object has a toggle (whether or not
self.lastx is None
) and it uses this toggle state
to remember whether it should generate a Unew or whether it should return
1-Uold. The generator on the other hand is written with `natural' program logic,
and Python does the work of keeping track of the state.
The end of a generator
The two generators you've seen so far are capable of generating an infinite sequence of values. Python also supports generators for finite sequences. When you `fall off the end', it raises an exception.def splitwords(words): for w in words.split(' '): yield w >>> g = splitwords('Network performance is fun') >>> print g.next() Network >>> print g.next() performance >>> print g.next() is >>> print g.next() fun >>> print g.next() Traceback (most recent call last): File "This sort of generator is often used for", line 1, in StopIteration >>>
for
loops, and
in list comprehensions (see below). The loop stops when the generator comes to its end.
def inputwords(prompt): try: while True: s = raw_input(prompt) if s=='Q': break elif len(s)==0: print 'Type Q to stop' else: yield s except EOFError: pass >>> res = [i for i in inputwords(': ')] : see : Jane : Type Q to stop : run : Q >>> print res ['see', 'Jane', 'run'] >>>
Tuples and lists
A tuple is a fixed-length list, e.g. a pair. You indicate a tuple by putting several elements together with brackets around and commas in between (although actually the brackets are optional).>>> p = (5,2) >>> q = 5,2 >>> print p,'and',q (5, 2) and (5, 2) >>>You can use a tuple on the left hand side of
=
to assign several values at the same time.
>>> a,b,c = 'hello there world'.split(' ') >>> print a,'---',b,'---',c hello --- there --- world >>>A list is variable-length and modifiable, whereas a tuple is fixed-length and immutable.
>>> x = ['I','can','fly'] >>> x.append('on a plane') >>> print x ['I', 'can', 'fly', 'on a plane'] >>> >>> x = ('i','can','fly') >>> x.append('on a plane') Traceback (most recent call last): File "", line 1, in AttributeError: 'tuple' object has no attribute 'append' >>>
List comprehensions
A list comprehension is a syntactic shorthand for constructing a list by applying a function to elements of another list. Here are three ways to capitalize all words in a list; the last of these is called a list comprehension.x = ['i','can','fly','on','a','plane'] y = [] for i in range(0,len(x)): word = x[i] word = word.upper() y.append(word) y = [] for word in x: y.append(word.upper()) y = [word.upper() for word in x]You can also put in an
if
clause to select only certain elements:
vowels = ('a','e','i','o','u') y = [word.upper() for word in x if word.startswith(vowels)]