Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > Classes referencing each other

Reply
Thread Tools

Classes referencing each other

 
 
Manuel Bleichner
Guest
Posts: n/a
 
      09-01-2006
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
 
Reply With Quote
 
 
 
 
Duncan Booth
Guest
Posts: n/a
 
      09-01-2006
"Manuel Bleichner" <(E-Mail Removed)> 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 ]
 
Reply With Quote
 
 
 
 
Georg Brandl
Guest
Posts: n/a
 
      09-01-2006
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
 
Reply With Quote
 
Manuel Bleichner
Guest
Posts: n/a
 
      09-01-2006
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
 
Reply With Quote
 
John Machin
Guest
Posts: n/a
 
      09-01-2006
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

 
Reply With Quote
 
Manuel Bleichner
Guest
Posts: n/a
 
      09-01-2006
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
 
Reply With Quote
 
Manuel Bleichner
Guest
Posts: n/a
 
      09-01-2006
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

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
 
Reply With Quote
 
Dennis Lee Bieber
Guest
Posts: n/a
 
      09-01-2006
On Fri, 01 Sep 2006 12:45:52 +0200, "Manuel Bleichner"
<(E-Mail Removed)> 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://www.velocityreviews.com/forums/(E-Mail Removed) (E-Mail Removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (E-Mail Removed))
HTTP://www.bestiaria.com/
 
Reply With Quote
 
Manuel Bleichner
Guest
Posts: n/a
 
      09-04-2006
> 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

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
the design of classes referencing each other in the large c++ project puzzlecracker C++ 1 09-14-2008 10:59 AM
Problem referencing other classes from ASP.NET code behind class =?Utf-8?B?SWFuIE1lYWtpbg==?= ASP .Net 5 09-14-2006 09:58 AM
referencing classes in other projects Andy G ASP .Net 2 01-20-2005 10:27 PM
objects referencing each other - how to implement? Angus Parvis Java 12 07-30-2004 10:32 PM
2 classes holding instances of each other ( but it won't work :( ) Gizmo C++ 2 10-29-2003 02:41 PM



Advertisments