1

I was curious about using static variables in python, and ended up at: Why doesn't Python have static variables?

In the accepted answer, which I found informative, it is said "If you want your function's behavior to change each time it's called, what you need is a generator".

However I was a bit confused, since the example used there can be done with a class as well:

class Foo(object):                                                              
    def __init__(self, bar):                                                    
        self.bar = bar                                                          

    def __call__(self):                                                         
        self.bar = self.bar * 3 % 5                                             
        return self.bar

foo = Foo(bar)                                                                  
print foo()                                                                     
print foo()

This makes more sense to me (but probably only because I haven't used generators properly before).

So my question is whether there is some other advantage to using generators over classes when the function needs to change behavior each time it's called.

Community
  • 1
  • 1
oadams
  • 3,019
  • 6
  • 30
  • 53

4 Answers4

3

Generators (the simple ones, not getting into coroutines) have a single, very specific purpose: Writing iterators. To this end, you get implicit capture of local variable and a keyword to suspend (as opposed to "terminate" - you can resume) execution and give the caller the next value of the iterator. The advantages are:

  • The logic reads more naturally, as you don't need to chop it into several method invokations - all state is preserved, including the program counter - you can use control flow across yielding . You could replace yield x with results.append(x) and return results at the end and get a function that's equivalent to list(the_original_generator(...)).
  • You don't need to explicitly make values long-lived by putting them into self - less to type and less to read.
  • You don't need a class declaration with two method declarations (a few lines of boilerplate, which is considerable by Python standards).

You can use the "capture of local variable" thing to implement an iterable which behaves as, except that you can have multiple ones and you don't invoke a function but use an iterator (next_val = next(vals) instead of next_val = vals()). Whether it is appropriate depends.

2

There's nothing you can do with a generator that you couldn't do with a class.

The reason to use generators is simply that they're shorter, and in many cases read more naturally than classes. Obviously this depends what you're doing. In the example you give, you just want to repeatedly perform the same operations and spit out the number, so a generator makes perfect sense. For more complex situations, you'd use a class.

Thomas K
  • 39,200
  • 7
  • 84
  • 86
  • 2
    I'd argue otherwise - for many complex tasks, generators have even bigger advantages. Think `if condition: yield a; yield b; else: yield c; yield d` - this requires at least an additional instance variable when written as class. For objects that are *only* iterators, generates are nearly always superios, IMHO. Only when there are other tasks besides iterating, classes shine. (And even then it may be worthwhile to write `__iter__` as a generator.) –  Oct 16 '11 at 11:52
  • @delnan: I think we mean the same thing. You *could* do the same thing with a class, but it would be longer and less clear. – Thomas K Oct 16 '11 at 12:10
  • It seems we just disagree on what a "complex situation" is - if you take it as "anything beyond an iterator", then yes, I agree. –  Oct 16 '11 at 12:18
  • @delnan: Yes, that's about it. – Thomas K Oct 16 '11 at 12:34
2

A proper generator class looks like this:

class Foo(object):                                                              
    def __init__(self, bar):                                                    
        self.bar = bar                                                          

    def __iter__(self):
        return self

    def next(self):
        self.bar = self.bar * 3 % 5                                             
        return self.bar

foo = Foo(3)                                                                  
print next(foo)                                                                     
print next(foo)

The equivalent generator function takes half the code:

def Foo(bar):
    while True:
        bar = bar * 3 % 5   
        yield bar                                                          

foo = Foo(3)                                                                  
print next(foo)                                                                     
print next(foo)

Generator functions are just a shortcut for the most common, basic iteration pattern. When you need something more complex a class allows for that.

melwil
  • 2,547
  • 1
  • 19
  • 34
Jochen Ritzel
  • 104,512
  • 31
  • 200
  • 194
  • The idea of doing it "properly" being that it fits in the standard way. Then you can do `iter(foo)`, `for n in foo:`, &c. If a language has standards, you should normally follow them. – Chris Morgan Oct 16 '11 at 13:54
1

For generators there's no other way to access the variable, except by calling a function, while in a class-based variant, you can modify it directly. This can be both advantage and disadvantage depending on your use-case. Also the code is simpler. For a more detailed discussion see: http://wiki.python.org/moin/Generators

Vsevolod Dyomkin
  • 9,343
  • 2
  • 31
  • 36