Classes referencing each other

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Manuel Bleichner

    Classes referencing each other

    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
  • Duncan Booth

    #2
    Re: Classes referencing each other

    "Manuel Bleichner" <manuel@prolink .dewrote:
    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().value s():
    if (type(cls) is type and
    hasattr(cls, 'connected_to') and
    callable(cls.co nnected_to)):
    cls.connected_t o = cls.connected_t o()

    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().value s():
    if (type(cls) is type and
    hasattr(cls, 'connected_to') ):
    cls.connected_t o = [globals()[c] for c in cls.connected_t o ]

    Comment

    • Georg Brandl

      #3
      Re: Classes referencing each other

      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

      Comment

      • Manuel Bleichner

        #4
        Re: Classes referencing each other

        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(co nnected_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().value s():
        if (type(cls) is type and
        hasattr(cls, 'connected_to') ):
        cls.connected_t o = [globals()[c] for c in cls.connected_t o ]
        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

        Comment

        • John Machin

          #5
          Re: Classes referencing each other

          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.connec ted_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

          Comment

          • Manuel Bleichner

            #6
            Re: Classes referencing each other

            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(CableConnecte d, WLANConnected):
            name = 'I am class A'
            cable_connectio ns = [B, C]
            wlan_connection s = [D]

            class B(CableConnecte d, RadioConnected) :
            name = 'I am class B'
            cable_connectio ns = [C, A]
            radio_connectio ns = [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

            Comment

            • Manuel Bleichner

              #7
              Re: Classes referencing each other

              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(Buildi ng, Researcher, Constructor, OnLand):
              maxhitpoints = 1000
              researchspeed = 70
              constructspeed = 50
              research_costs = [SmallEM, Weapons, Shields]
              resource_costs = [Iron: 500, Granite: 300, Silicon: 150]
              possible_resear ch = [MediumEM, EMWeapons, EmShockwave]
              possible_constr ucts = [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_constr ucts and
              possible_resear ch.

              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

              Comment

              • Manuel Bleichner

                #8
                Re: Classes referencing each other

                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

                Comment

                Working...