Suggesting a new feature - "Inverse Generators"

Peter Otten __peter__ at web.de
Sat Mar 26 03:04:57 EST 2005


Jordan Rastrick wrote:

>> No, it's nothing special about groupby.  record simply stores its
> state in a
>> mutable default parameter.  This isn't general good practice: at
> least you have
>> to be careful with it.  You can see the behavior in the following
> example:
>>   >>> def accumulate(value, accum = []):
>>   ...     accum.append(value)
>>   ...     return accum
>>   ...
>>   >>> accumulate(1)
>>   [1]
>>   >>> accumulate(2)
>>   [1, 2]
>>   >>> accumulate(6)
>>   [1, 2, 6]
>>   >>>
> 
> Wow.... I'd never seen this kind of thing in examples of Python code.
> Although its really neat, it doesn't really make sense, intuituvely to
> me. Why does accum remember its state - I suppose its to do with the
> scope of arguments (as opposed to method variables) or something like
> that?

Michael's accumulator uses the fact that default arguments are only
evaluated once -- when the function is created. The behaviour shown above
is actually a common trap every newbie has to experience once until he
learns the workaround:

def accumulate(value, a=None):
    if a is None:
        a = []
    a.append(value)
    return a

> Still, thats powerful. But I see why its not standard use -  it could
> be easily abused!

There are limitations, too. If you want more than one accumulator you have
to pass the accum argument explicitly or wrap accumulate() into a factory:

def make_accumulator():
    def accumulate(value, a=[]):
        a.append(value)
        return a
    return accumulate

Sill, you cannot get hold of the result of the accumulation without
modifying it. One way to fix that:

>>> def make_accumulator():
...     a = []
...     def accumulate(value):
...             a.append(value)
...     return a, accumulate
...
>>> items1, accu1 = make_accumulator()
>>> for i in range(4): accu1(i)
...
>>> items1
[0, 1, 2, 3]
>>> items2, accu2 = make_accumulator()
>>> for i in "abc": accu2(i)
...
>>> items2
['a', 'b', 'c']
>>> items1
[0, 1, 2, 3]

Now this is all nice and dandy to play around with and learn something about
Python's scoping rules, but you can get the same functionality in a
straightforward way with a callable object (like Bengt Richter's Grouper)
and that is what I would recommend.

Peter





More information about the Python-list mailing list