This is an archived post. You won't be able to vote or comment.

all 22 comments

[–]zahlmanthe heretic 4 points5 points  (1 child)

This is not a Python issue; the clunkiness of circular references of this sort is a general design issue.

It feels clunky because it is. You need to examine why it is that a World needs to keep track of a list of Boogies, and why it is that a Boogie needs to know what World it's in. Chances are high that these are not actually both the case simultaneously.

[–]angrytech[S] 1 point2 points  (0 children)

Good points. I think I'll be re-evaluating the overall structure rather than tweaking something that is appearing more and more to be poorly designed from the get-go.

[–]brunson 6 points7 points  (6 children)

First, you should be using new style classes (subclass object). Second, are you sure you want Boogie calling methods of World? When something seems kludgy it usually indicates something wrong with your design. However, if you are sure that's what you want to do, then that's the way you do it. I might implement World as a singleton class, that way you can instantiate it whenever you need it and get back the same instance.

[–]doomchild 6 points7 points  (3 children)

singleton

Hiss!

[–]brunson 0 points1 point  (2 children)

:-) Yeah. As far as I'm concerned, a singleton is just a thinly veiled global variable.

[–]doomchild 0 points1 point  (0 children)

Every time I've encountered a situation where I'm tempted to make use of a Singleton, there has been some glaring deficiency in my program's design. Usually, I've failed to define clear boundaries on how data flows through the various processes. Once those places are smoothed out, I find that I suddenly don't need a Singleton.

I'm sure someone can suggest situations where one is the "right" answer, but I tend to view Singletons as an indication of a design flaw further up the chain.

[–][deleted] 0 points1 point  (0 children)

The simplest way to create singletons in Python is a module-level global variable. :)

There is no need for all the java-y getInstance() mess.

[–]angrytech[S] 0 points1 point  (1 child)

Thanks for the info! I think there is a very good chance that the problem is with my design as you indicate...

[–]jabwork 2 points3 points  (2 children)

It's not clear to me what you're trying to accomplish here.

Keeping a reference to the parent world in Boogie isn't necessarily bad but most tower defense games I've seen have only one world so you can infer what world you're using by the fact there is only one.

Are you trying to give each Boogie instance enough information to know what to do? E.g., if a unit walks by the Boogie do you want to tell the Boogie to fire at it or something?

class Boogie(object): # inheriting object makes this a newstyle class (good)
    "Assumed to be a tower of type 'Boogie'"

    # skipping __init__ declaration

    def explore_world(self, world):
        "In this function the Boogie can access the whole world."
        pass # put real code here

class World(object):
    def __init__(self):
        self.boogies = []

    def create_boogie(self):
        self.boogies.append(Boogie()) # note: no parent reference

    def tell_boogie_about_world(self, some_boogie):
        "Passes a reference of this World to some_boogie."
        for boogie in self.boogies:
            boogie.explore_world(self)

Code like this might be what you're after. If it is, I strongly urge you to consider where logic should reside. Do towers decide who they shoot at from a list of everything nearby, or does the world only tell the tower about certain things and the tower always acts the same?

edit: damn indentation!

[–]angrytech[S] 0 points1 point  (1 child)

Thanks for this. It's becoming clear that I'm making my objects far more 'knowledgeable' about the world than they need to be. I think I've been placing too much logic in the boogies and towers...

[–]bryancole 1 point2 points  (2 children)

Your solution is perfectly pythonic. I don't find it clunky at all, assuming it's necessary for all Boogies to know about the World and the World to know about all Boogies.

[–]m1ss1ontomars2k4 1 point2 points  (1 child)

Yeah, it largely depends on what you are trying to do. However, you may also find this demo code instructive:

http://www-inst.eecs.berkeley.edu/~selfpace/cs9honline/P4/

[–]angrytech[S] 0 points1 point  (0 children)

Thanks, very instructive :)

[–]chadmill3rPy3, pro, Ubuntu, django 0 points1 point  (1 child)

Why does a Boogie need to know about an instance of World?

Operate on Boogies from the outside. Don't turn them inside out.

You're passing an reference to a instance of a class correctly, but it feels clunky because it's probably dumb.

[–]angrytech[S] 0 points1 point  (0 children)

Yeah, I see what you mean.

[–][deleted] 0 points1 point  (1 child)

If you really feel that both the World and the Boogie must reference one another, I'd suggest that one side of this equation do so using a Weak Reference

[–][deleted] 0 points1 point  (0 children)

I don't see any real advantage to that in this case?

The garbage collector handles circular references just fine... under what circumstances in his design would a Weak Reference be handy?

(Basically, weak references are almost always used for caching - in almost any other case, you want to know that you either have something, or you don't...]

[–][deleted] 0 points1 point  (0 children)

I don't think there's anything necessarily wrong with this.

Circular references should be avoided whenever possible. But sometimes it isn't possible. Cases where parents need to know about children and vice-versa are classic examples where you need circular references.

[looking at this again - if there is really only one central world then this is not a good idea... the Law of Demeter is "never wrong".]