[ACCEPTED]-python: attributes on a generator object-python

Accepted answer
Score: 20

Yes.

class Filter( object ):
    def __init__( self, content ):
        self.content = content
    def __call__( self, someParam ):
        self.someParam = someParam
        for line in self.content:
            if line == someParam:
                yield line

0

Score: 7

Unfortunately, generator objects (the results 10 returned from calling a generator function) do 9 not support adding arbitrary attributes. You 8 can work around it to some extent by using 7 an external dict indexed by the generator 6 objects, since such objects are usable as keys 5 into a dict. So where you'd like to do, say:

a = filter(23)
b = filter(45)
...
a.foo = 67
...
x = random.choice([a,b])
if hasattr(x, 'foo'): munge(x.foo)

you 4 may instead do:

foos = dict()
a = filter(23)
b = filter(45)
...
foos[a] = 67
...
x = random.choice([a,b])
if x in foos: munge(foos[x])

For anything fancier, use 3 a class instead of a generator (one or more 2 of the class's methods can be generators, after 1 all).

Score: 3

If you want to interrogate them for debugging 5 purposes, then the following function will 4 help:

import inspect

def inspect_generator(g):
    sourcecode = open(g.gi_code.co_filename).readlines()
    gline = g.gi_code.co_firstlineno
    generator_code = inspect.getblock(sourcecode[gline-1:])

    output = "Generator %r from %r\n" % (g.gi_code.co_name, g.gi_code.co_filename)
    output += "".join("%4s: %s" % (idx+gline, line) for idx, line in enumerate(generator_code))

    output += "Local variables:\n"
    output += "".join("%s = %r\n" % (key,value) for key,value in g.gi_frame.f_locals.items())

    return output

print inspect_generator(filter(6))
"""Output:
Generator 'filter' from 'generator_introspection.py'
   1: def filter(x):
   2:     for line in myContent:
   3:         if line == x:
   4:             yield x
Local variables:
x = 6
"""

If you want to interrogate them to 3 implement functionality then classes implementing 2 the iterator protocol are probably a better 1 idea.

Score: 2

No. You can't set arbitrary attributes 7 on generators.

As S. Lott points out, you 6 can have a object that looks like a generator, and 5 acts like a generator. And if it looks like 4 a duck, and acts like a duck, you've got 3 yourself the very definition of duck typing, right 2 there.

It won't support generator attributes 1 like gi_frame without the appropriate proxy methods, however.

Score: 0

Thinking about the problem, there is a way 9 of having generators carry around a set 8 of attributes. It's a little crazy--I'd 7 strongly recommend Alex Martelli's suggestion 6 instead of this--but it might be useful 5 in some situations.

my_content = ['cat', 'dog days', 'catfish', 'dog', 'catalog']

def filter(x):
    _query = 'I\'m looking for %r' % x

    def _filter():
        query = yield None
        for line in my_content:
            while query:
                query = yield _query

            if line.startswith(x):
                query = yield line

        while query:
            query = yield _query

    _f = _filter()
    _f.next()
    return _f

for d in filter('dog'):
    print 'Found %s' % d

cats = filter('cat')
for c in cats:
    looking = cats.send(True)
    print 'Found %s (filter %r)' % (c, looking)

If you want to ask the 4 generator what it's filtering on, just call 3 send with a value that evaluates to true. Of 2 course, this code is probably too clever 1 by half. Use with caution.

Score: 0

I realize this is a very belated answer, but...

Instead 4 of storing and later reading some additional 3 attribute, your code could later just inspect 2 the generator's variable(s), using:

filter.gi_frame.f_locals

I 1 guess Ants Aasma hinted at that.

More Related questions