Classes referencing each other

Discussion in 'Python' started by Manuel Bleichner, Sep 1, 2006.

  1. Hello list,

    I have searched for some time now, but no result...
    I'm having the following problem:

    In a module I have a huge number of classes of the form:

    class A(object):
    connected_to = [B, C]
    <other attributes...>

    class B(object)
    connected_to = [C]
    <other attributes...>

    class C(object)
    connected_to = [A]
    <other attributes...>

    As you see, classes A and B reference classes that
    are not yet defined when the class is being defined.
    It will raise a NameError: 'B'.

    I know i could solve this by leaving out the definition
    of 'connected_to' in A and attach it to the class later on by
    A.connected_to = [B, C]
    but I would like to avoid this, because in the module
    there are about 50 classes that have to be altered from time
    to time and it's just incredibly ugly if I have to look for
    the attribute definitions in more than one place.

    Also, I would like to avoid eval(), because the references
    have to be followed very often and that would cause more
    CPU load and make the program code uglier :)

    If anyone of you knows a neat way to solve this, I'd be
    very grateful.

    Greetings,
    Manuel
    Manuel Bleichner, Sep 1, 2006
    #1
    1. Advertising

  2. Manuel Bleichner

    Duncan Booth Guest

    "Manuel Bleichner" <> wrote:

    > If anyone of you knows a neat way to solve this, I'd be
    > very grateful.


    You could use a function:

    class A(object):
    @staticmethod
    def connected_to(): return [B, C]
    <other attributes...>

    class B(object)
    @staticmethod
    def connected_to(): return [C]
    <other attributes...>

    class C(object)
    @staticmethod
    def connected_to(): return [A]
    <other attributes...>

    for cls in globals().values():
    if (type(cls) is type and
    hasattr(cls, 'connected_to') and
    callable(cls.connected_to)):
    cls.connected_to = cls.connected_to()

    or just store the names of the classes and do a similar fixup once they are
    all defined:

    class A(object):
    connected_to = ['B', 'C']
    <other attributes...>

    for cls in globals().values():
    if (type(cls) is type and
    hasattr(cls, 'connected_to')):
    cls.connected_to = [globals()[c] for c in cls.connected_to ]
    Duncan Booth, Sep 1, 2006
    #2
    1. Advertising

  3. Manuel Bleichner

    Georg Brandl Guest

    Manuel Bleichner wrote:
    > Hello list,
    >
    > I have searched for some time now, but no result...
    > I'm having the following problem:
    >
    > In a module I have a huge number of classes of the form:
    >
    > class A(object):
    > connected_to = [B, C]
    > <other attributes...>
    >
    > class B(object)
    > connected_to = [C]
    > <other attributes...>
    >
    > class C(object)
    > connected_to = [A]
    > <other attributes...>
    >
    > As you see, classes A and B reference classes that
    > are not yet defined when the class is being defined.
    > It will raise a NameError: 'B'.
    >
    > I know i could solve this by leaving out the definition
    > of 'connected_to' in A and attach it to the class later on by
    > A.connected_to = [B, C]
    > but I would like to avoid this, because in the module
    > there are about 50 classes that have to be altered from time
    > to time and it's just incredibly ugly if I have to look for
    > the attribute definitions in more than one place.


    You could move all connections to a central location after the
    class definitions, such as

    class A: pass
    class B: pass
    class C: pass

    connections = {A: (B, C), B: (C,), C: (A,)}

    Georg
    Georg Brandl, Sep 1, 2006
    #3
  4. Thanks for your answer :)

    > You could use a function:
    >
    > class A(object):
    > @staticmethod
    > def connected_to(): return [B, C]
    > <other attributes...>


    I already thought about such a solution, but since
    my code has to be compatible with python 2.3, i would
    have to use the connected_to = staticmethod(connected_to)
    syntax, which bloats the code and makes it quite unreadable.


    > or just store the names of the classes and do a similar fixup once they
    > are
    > all defined:
    >
    > class A(object):
    > connected_to = ['B', 'C']
    > <other attributes...>
    >
    > for cls in globals().values():
    > if (type(cls) is type and
    > hasattr(cls, 'connected_to')):
    > cls.connected_to = [globals()[c] for c in cls.connected_to ]


    This solution seems good to me. It won't work for me as it is,
    because the structure of the classes is actually a bit more
    complex, but it pushed me in the right direction.

    Thanks again,
    Manuel
    Manuel Bleichner, Sep 1, 2006
    #4
  5. Manuel Bleichner

    John Machin Guest

    Manuel Bleichner wrote:
    > Hello list,
    >
    > I have searched for some time now, but no result...
    > I'm having the following problem:
    >
    > In a module I have a huge number of classes of the form:
    >
    > class A(object):
    > connected_to = [B, C]
    > <other attributes...>
    >
    > class B(object)
    > connected_to = [C]
    > <other attributes...>
    >
    > class C(object)
    > connected_to = [A]
    > <other attributes...>
    >
    > As you see, classes A and B reference classes that
    > are not yet defined when the class is being defined.
    > It will raise a NameError: 'B'.
    >
    > I know i could solve this by leaving out the definition
    > of 'connected_to' in A and attach it to the class later on by
    > A.connected_to = [B, C]
    > but I would like to avoid this, because in the module
    > there are about 50 classes that have to be altered from time
    > to time and it's just incredibly ugly if I have to look for
    > the attribute definitions in more than one place.


    So why can't you do it all in one place by
    A.connected_to = [B, C]
    B.connected_to = [C]
    C.connected_to = [A]
    ?

    or by a tabular method:

    connections = (
    (A, [B, C]),
    (B, [C]),
    (C, [A]),
    )
    for cls_from, targets in connections:
    # maybe insert some checking code in here ...
    # is X.connected_to = [X] allowed?
    # is Y.connected_to = [] allowed?
    cls_from.connected_to = targets

    >
    > Also, I would like to avoid eval(), because the references
    > have to be followed very often and that would cause more
    > CPU load and make the program code uglier :)


    Avoiding eval() does always seem to be a good thing :)

    BTW, care to tell us what the connections mean? Applications of
    connected instances of the one class are of course very common, but 50
    classes connected in a cyclic fashion is rather new to me ...

    Cheers,
    John
    John Machin, Sep 1, 2006
    #5
  6. Thanks for the answer.

    > You could move all connections to a central location after the
    > class definitions, such as
    >
    > class A: pass
    > class B: pass
    > class C: pass
    >
    > connections = {A: (B, C), B: (C,), C: (A,)}



    I think I simplified my classes a bit too much :)
    Actually there are multiple types of connections between
    the classes. for example:

    class A(CableConnected, WLANConnected):
    name = 'I am class A'
    cable_connections = [B, C]
    wlan_connections = [D]

    class B(CableConnected, RadioConnected):
    name = 'I am class B'
    cable_connections = [C, A]
    radio_connections = [F]
    ....

    And because not all classes have the same types of connections,
    it would become extremely unreadable to try to define them
    at a central place. Also it seperates attributes from each other;
    'name' is defined in the class, the connections somewhere else.

    Greetings,
    Manuel
    Manuel Bleichner, Sep 1, 2006
    #6
  7. Hi, thanks for your answer, too :)

    Your solution won't do it, because of reasons I explained
    in the answer to Georg Brandl.

    > BTW, care to tell us what the connections mean? Applications of
    > connected instances of the one class are of course very common, but 50
    > classes connected in a cyclic fashion is rather new to me ...


    Okay, I didn't want to do this, because some of you would
    think I'm crazy, but what the heck :D

    I'm programming a browsergame which is split in multiple parts.
    There is a core which is running all the time and a cgi client connecting
    to it. The core maintains all classes and keeps the game running, while
    the client only requests data from it.
    The core of course needs to hold all the definitions for the game
    (e.g. units, buildings, resources, research) - these are written as
    classes.

    class EMCenter(Building, Researcher, Constructor, OnLand):
    maxhitpoints = 1000
    researchspeed = 70
    constructspeed = 50
    research_costs = [SmallEM, Weapons, Shields]
    resource_costs = [Iron: 500, Granite: 300, Silicon: 150]
    possible_research = [MediumEM, EMWeapons, EmShockwave]
    possible_constructs = [Razor, Lancer, Stinger] # these are
    all units

    Now when a certain unit should be built, it must be checked if
    the requirements are matched.
    => check research_costs and resource_costs
    After building it, an instance of the class will be created.
    The new object can itself build (=> Constructor) and research
    (=> Researcher) the items provided in possible_constructs and
    possible_research.

    The problem occured when i wanted to define a unit that can
    construct a building which itself is able to construct that
    unit.

    Absolutely overkill for a browsergame, I know, but I like the concept =)


    Manuel
    Manuel Bleichner, Sep 1, 2006
    #7
  8. On Fri, 01 Sep 2006 12:45:52 +0200, "Manuel Bleichner"
    <> declaimed the following in comp.lang.python:

    >
    > I'm programming a browsergame which is split in multiple parts.
    > There is a core which is running all the time and a cgi client connecting
    > to it. The core maintains all classes and keeps the game running, while
    > the client only requests data from it.
    > The core of course needs to hold all the definitions for the game
    > (e.g. units, buildings, resources, research) - these are written as
    > classes.
    >


    > class EMCenter(Building, Researcher, Constructor, OnLand):


    Does the "EMCenter" really need to /subclass/ from all of those? Or
    would it be better to make some of those attributes of an EMCenter
    INSTANCE (passing in instances of the others to the __init__() ).

    class EMCenter(object):
    def __init__(self, b, r, c, ol):
    self.building = b
    self.researcher = r
    ...

    IOW: the difference between:
    an EMCenter IS a building AND
    IS a researcher AND
    IS a constructor AND
    IS a "OnLand"
    vs
    an EMCEnter HAS a building AND
    HAS a researcher AND
    HAS a constructor AND
    HAS a "OnLand"

    Note that "OnLand" already sounds like an attribute -- and at that,
    maybe an attribute of "Building"


    > The problem occured when i wanted to define a unit that can
    > construct a building which itself is able to construct that
    > unit.
    >

    As soon as you get a recursive definition, you MUST split... (Ever
    notice how many strongly typed languages have "forward definition"
    forms:

    type Node_Link;
    type Node is record
    Previous : Node_Link;
    Next : Node_Link;
    Data : ...
    end record;
    type Node_Link is access Node;

    Essentially, you'll have to split the creation of such an object
    into the creation of an empty "building", create the "unit" passing it
    the empty building, then modify the passed-in "building" to know about
    the "unit".

    Python doesn't have declarations, so this all has to be performed as
    run-time operations... Probably by passing in "None" to the
    initialization of the object (not to the definition of the class), then
    using either direct reference (second.first.link = second) or by
    defining all the classes to have a setter for this.... Since the "unit"
    constructs "buildings", and the building constructs "units", they are
    both "constructors"

    class Constructor(object):
    def __init__(self, name, product= None):
    self._name = name
    self._product = product #need to extract parent class of product
    def configure(self, product):
    self._product = product
    def construct(self, id):
    return self._product(id)
    def name(self):
    return self._name

    class Unit(Constructor):
    pass

    class Building(Constructor):
    pass

    bUnit = Unit("Archtype") #create "empty" unit
    uBuilding = Building("Nursery", bUnit) #create unit making building
    bUnit.configure(uBuilding) #update unit with building

    NOTE: I need to get to work, so don't have time to study the docs...
    I expect, for your recursive constructors you'll need to overload the
    construct() to invoke the proper .configure() and somehow
    retrieve/pass-in the parent class.
    --
    Wulfraed Dennis Lee Bieber KD6MOG

    HTTP://wlfraed.home.netcom.com/
    (Bestiaria Support Staff: )
    HTTP://www.bestiaria.com/
    Dennis Lee Bieber, Sep 1, 2006
    #8
  9. > Does the "EMCenter" really need to /subclass/ from all of those? Or
    > would it be better to make some of those attributes of an EMCenter
    > INSTANCE (passing in instances of the others to the __init__() ).
    >
    > class EMCenter(object):
    > def __init__(self, b, r, c, ol):
    > self.building = b
    > self.researcher = r
    > ...
    >
    > IOW: the difference between:
    > an EMCenter IS a building AND
    > IS a researcher AND
    > IS a constructor AND
    > IS a "OnLand"


    => These relations are correct:
    the EM Center can build units and research technologies, therefore
    it is a researcher and constructor.


    > Note that "OnLand" already sounds like an attribute -- and at that,
    > maybe an attribute of "Building"


    Yes, this one is really an attribute, but it's not yet implemented and
    I wasn't sure how to do it when I wrote the template code.


    > As soon as you get a recursive definition, you MUST split...


    Yes =(
    I'll have to try to reduce such relations to a minimum...
    It would have just been nice to declare and later define the classes,
    but such a feature would bloat the neat python syntax.

    Thanks for all the text you wrote, but it all went a bit in the
    wrong direction, sorry :)


    Manuel
    Manuel Bleichner, Sep 4, 2006
    #9
    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. Andy G
    Replies:
    2
    Views:
    326
    Peter Rilling
    Jan 20, 2005
  2. =?Utf-8?B?SWFuIE1lYWtpbg==?=

    Problem referencing other classes from ASP.NET code behind class

    =?Utf-8?B?SWFuIE1lYWtpbg==?=, Feb 18, 2005, in forum: ASP .Net
    Replies:
    5
    Views:
    940
    tittlejoseph
    Sep 14, 2006
  3. Angus Parvis
    Replies:
    12
    Views:
    645
    xarax
    Jul 30, 2004
  4. Gizmo
    Replies:
    2
    Views:
    277
    Christian Jaeger
    Oct 29, 2003
  5. puzzlecracker
    Replies:
    1
    Views:
    294
    Erik Wikström
    Sep 14, 2008
Loading...

Share This Page