[ACCEPTED]-python: attributes on a generator object-python
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
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).
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.
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.
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.
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:
I 1 guess Ants Aasma hinted at that.
More Related questions