trouble with generators

Discussion in 'Python' started by Hans-Peter Jansen, May 10, 2007.

  1. 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.:

    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__)

    class GenA(Gen):
    def __init__(self):
    self.genB = GenB()

    def records(self):
    for a in Gen.records(self, 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())

    # 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>
    # 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>

    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?

    TIA, Pete
     
    Hans-Peter Jansen, May 10, 2007
    #1
    1. Advertising

  2. 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__)
    >
    > 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
     
    Diez B. Roggisch, May 10, 2007
    #2
    1. Advertising

  3. In <f1v4in$4qj$01$-online.com>, Hans-Peter Jansen wrote:

    > class Gen(object):
    > def records(self, cls):
    > for i in range(3):
    > setattr(cls, "id", "%s%s" % (cls.__doc__, i))
    > yield cls
    >
    > […]
    >
    > class GenA(Gen):
    > def __init__(self):
    > self.genB = GenB()
    >
    > def records(self):
    > for a in Gen.records(self, A()):


    Here you create an instance of `A` and pass that *instance* and not the
    *class*. If you would pass the class here, you must create objects in
    `Gen.records()`.

    Ciao,
    Marc 'BlackJack' Rintsch
     
    Marc 'BlackJack' Rintsch, May 10, 2007
    #3
  4. Hi Diez,

    first, thanks for your comprehensive answer.

    Diez B. Roggisch wrote:

    > Hans-Peter Jansen schrieb:
    >>
    >> 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?


    Yes, exactly. Sorry for the confusion..

    >
    > 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]
    >
    > 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))


    Yes, that did the trick. Silly me. Rookie error. Here's what I was after:

    #!/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):
    "Gen"
    def records(self, cls):
    for n in range(3):
    i = cls()
    i.id = "%s%s" % (i.__doc__, n)
    yield i

    class GenA(Gen):
    def __init__(self):
    self.genB = GenB()

    def records(self):
    for a in Gen.records(self, A):
    for b in self.genB.records():
    a.b.append(b)
    yield a

    class GenB(Gen):
    def records(self):
    return Gen.records(self, B)

    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

    created pretty nice different _instances_ of what I wanted:
    0 B0 <__main__.B object at 0xb7cacfec>
    1 B1 <__main__.B object at 0xb7cae04c>
    2 B2 <__main__.B object at 0xb7cae08c>

    0 A0 <__main__.A object at 0xb7cae12c>
    B0 <__main__.B object at 0xb7cae1ac>
    B1 <__main__.B object at 0xb7cae1ec>
    B2 <__main__.B object at 0xb7cae22c>
    1 A1 <__main__.A object at 0xb7cae16c>
    B0 <__main__.B object at 0xb7cae2ac>
    B1 <__main__.B object at 0xb7cae2ec>
    B2 <__main__.B object at 0xb7cae32c>
    2 A2 <__main__.A object at 0xb7cae26c>
    B0 <__main__.B object at 0xb7cae3ac>
    B1 <__main__.B object at 0xb7cae3ec>
    B2 <__main__.B object at 0xb7cae42c>

    Didn't found the forest because all the trees. Thanks again.

    Greetings to Berlin,
    Pete
     
    Hans-Peter Jansen, May 10, 2007
    #4
  5. Marc 'BlackJack' Rintsch wrote:

    > In <f1v4in$4qj$01$-online.com>, Hans-Peter Jansen wrote:
    >
    >> class Gen(object):
    >> def records(self, cls):
    >> for i in range(3):
    >> setattr(cls, "id", "%s%s" % (cls.__doc__, i))
    >> yield cls
    >>
    >> […]
    >>
    >> class GenA(Gen):
    >> def __init__(self):
    >> self.genB = GenB()
    >>
    >> def records(self):
    >> for a in Gen.records(self, A()):

    >
    > Here you create an instance of `A` and pass that *instance* and not the
    > *class*. If you would pass the class here, you must create objects in
    > `Gen.records()`.


    Yes, that was my fault, as you both found.

    > Ciao,
    > Marc 'BlackJack' Rintsch


    Thanks, Marc.

    Cheers, Pete
     
    Hans-Peter Jansen, May 10, 2007
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Hypo
    Replies:
    6
    Views:
    463
  2. Rhino
    Replies:
    4
    Views:
    5,786
    Roedy Green
    Jan 13, 2006
  3. Pavel

    Code Generators

    Pavel, May 14, 2006, in forum: Java
    Replies:
    7
    Views:
    727
    dimitar
    May 19, 2006
  4. Mark

    Memoizing Generators

    Mark, Jul 8, 2003, in forum: Python
    Replies:
    2
    Views:
    352
  5. Duncan Booth

    generators improvement

    Duncan Booth, Aug 19, 2003, in forum: Python
    Replies:
    4
    Views:
    295
    Oleg Leschov
    Aug 20, 2003
Loading...

Share This Page