Classes referencing each other

  • Thread starter Manuel Bleichner
  • Start date
M

Manuel Bleichner

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
 
D

Duncan Booth

Manuel Bleichner said:
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 ]
 
G

Georg Brandl

Manuel said:
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
 
M

Manuel Bleichner

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
 
J

John Machin

Manuel said:
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
 
M

Manuel Bleichner

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
 
M

Manuel Bleichner

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
 
D

Dennis Lee Bieber

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
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/
 
M

Manuel Bleichner

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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top