trouble with generators

Diez B. Roggisch deets at nospam.web.de
Thu May 10 11:26:52 EDT 2007


Hans-Peter Jansen schrieb:
> Hi Pythonistas,
> 
> I'm stuck in a maze of new style classes and generators. While I love the
> concepts, I obviously didn't grok them throughout. 
> 
> I'm trying to generate a bunch of similar classes, where some are contained
> in list attributes of others, e.g.:  

All your code below shows that you don't create classes, but _instances_ 
of classes. So - is that what you mean to do? Or do you really want to 
create classes?

> class A:
>   def __init__(self):
>     self.id = 'A1'
>     self.b = [instances of B]
> 
> class B:
>   def __init__(self):
>     self.id = 'B1'
> 
> Here's the test code, I have:
> 
> #!/usr/bin/env python
> # -*- coding: utf8 -*-
> 
> class A(object):
>     "A"
>     def __init__(self):
>         self.id = None
>         self.b = []
> 
> class B(object):
>     "B"
>     def __init__(self):
>         self.id = None
> 
> class Gen(object):
>     def records(self, cls):
>         for i in range(3):
>             setattr(cls, "id", "%s%s" % (cls.__doc__,  i))
>             yield cls
> 
>     def display(self, rec):
>         for i in rec.__dict__.keys():
>             if not i.startswith("_"):
>                 print "%s: %s: %s" % (rec.__doc__, i, rec.__dict__[i])
> 
> class GenA(Gen):
>     def __init__(self):
>         self.genB = GenB()
>     
>     def records(self):
>         for a in Gen.records(self, A()):

Here you pass an instance of A, not the class A.

>             for b in self.genB.records():
>                 #self.genB.display(b)
>                 a.b.append(b)
>             #self.display(a)
>             yield a
> 
> class GenB(Gen):
>     def records(self):
>         return Gen.records(self, B())

Same here - instance of B.

> # testing..
> 
> aRecs = []
> bRecs = []
> 
> for i, r in enumerate(GenB().records()):
>     bRecs.append(r)
>     print i, r.id, r
> 
> for i, r in enumerate(GenA().records()):
>     aRecs.append(r)
>     print i, r.id, r
>     for b in r.b:
>         print b.id, b
> 
> 
> Here's the commented output:
> # even if I keep a reference to each rec, the object is reused:
> 0 B0 <__main__.B object at 0xb7bd0f8c>
> 1 B1 <__main__.B object at 0xb7bd0f8c>
> 2 B2 <__main__.B object at 0xb7bd0f8c>

Sure - because you create one B-instance, and pass that to your generator.

That generator then simply sets the succinct id's on that object, and 
returns it. But it is always the same instance!!

In a nutshell, you do this:

b = B()
res = []
for i in xrange(3):
    b.id = i
    res.append(b)

Always the same b.

What you seem to want is this

 >>> class B(object):
...    pass
...
 >>> res = []
 >>> for i in xrange(3):
...     class BSubClass(B):
...          pass
...     BSubClass.id = i
...     res.append(BSubClass)
...
 >>> print [c.id for c in res]
[0, 1, 2]
 >>>



> # same here, with additional quadratic behavior, I do not understand
> 0 A0 <__main__.A object at 0xb7bd206c>
> B2 <__main__.B object at 0xb7bd210c>
> B2 <__main__.B object at 0xb7bd210c>
> B2 <__main__.B object at 0xb7bd210c>
> 1 A1 <__main__.A object at 0xb7bd206c>
> B2 <__main__.B object at 0xb7bd210c>
> B2 <__main__.B object at 0xb7bd210c>
> B2 <__main__.B object at 0xb7bd210c>
> B2 <__main__.B object at 0xb7bd20ec>
> B2 <__main__.B object at 0xb7bd20ec>
> B2 <__main__.B object at 0xb7bd20ec>
> 2 A2 <__main__.A object at 0xb7bd206c>
> B2 <__main__.B object at 0xb7bd210c>
> B2 <__main__.B object at 0xb7bd210c>
> B2 <__main__.B object at 0xb7bd210c>
> B2 <__main__.B object at 0xb7bd20ec>
> B2 <__main__.B object at 0xb7bd20ec>
> B2 <__main__.B object at 0xb7bd20ec>
> B2 <__main__.B object at 0xb7bd0f8c>
> B2 <__main__.B object at 0xb7bd0f8c>
> B2 <__main__.B object at 0xb7bd0f8c>


It's not quadratic - you add the same B to a.b in each generator run, so 
the first run shows 3 times the B, then 6, then 9. And because its 
always the same instance, printing them yields the same id for all - B2.

> I expected to get 3 different class objects from both sections, with each A
> containing 3 different Bs in the latter section, but obviously got
> something else. 
> 
> Could some kind soul help me to distangle my mind twist here? Am I healable?

I'm still not sure what you want - do you want instances created, or 
classes? For the former, you need constructor calls on your classes, and 
pass the class instead of an instance. Like this:


class B(object):
    pass


def g(cls):
     for i in xrange(3):
         o = cls()
         o.id = i
         yield o

list(g(B))

Or you want really different classes (which is somewhat strange then to 
create that hierarchy of yours with As containing Bs), which you can 
accomplish using a class-statement in the generator as shown above. 
There are other ways as well, but less intuitive I'd say.


Maybe stepping back and telling us what you want to accomplish here 
would help

Diez




More information about the Python-list mailing list