Is it valid for two objects to have reciprocal references?

Asked

Viewed 55 times

1

Is this an acceptable practice or is it a gambit and shouldn’t you ever do it? Is there any solution to avoid this? It turns out that the player object needs to know what the state of the game is in order to play, but the game has it so it would duplicate itself in memory (maybe not in Python, but in another language)?

class Player(object):

    def __init__(self, game):
        ...
        self.game = game


class Game(object):

    def __init__(self):
        ...
        self.player = Player(self)
  • 2

    If it’s a good idea I don’t know, but the memory won’t duplicate because both variables will point to the same reference.

  • I was wondering how the player needs to know the state of the game to play. Who will call the play will not be the game itself? If so, he already has access to his own state.

  • 1

    I’m gonna have to rethink the whole system the way it’s never going forward.

  • @Mateuscardososilva congratulations, began to understand... :)

  • "I was wondering how the player needs to know the state of the game to play. " = there are many very common situations - if there is a map with walls in the game, for example, in a player’s movement method, he needs to have access to this map to know if it doesn’t hit a wall or another object. The alternative would be to pass the logic of moving the object to the Game class, which would be counterproductive - or pass a game reference in all calls to the Move method - which is not so bad (alias, I will update my answer with this)

1 answer

2


Yes - there are cases where reciprocal references naturally are the most practical way to solve various problems, and the one you arrived at is one of those.

What’s wrong with that? Not many - in a long life system, with a lot of creation and destruction of objects you can have cases where the Python collector Garbage will get lost and will "leak memory" - but even you will not have so many occurrences in a single game, and neither the Collector grabage gets lost so much. (In case you create new instances of the player in other "levels" and the player has himself callbacks to handle keyboard/mouse/joysitick events you could have more drastic side effects).

What does language have to ensure that you have no problems with reciprocal references? The "weakreferences" - are weak references in which, once the original object no longer exists, the referenced object points to the "empty" - this avoids potential problems of persisitndo objects in memory when no longer in use. I talk more of them down.

As for the "semantic problem" of - in case - the game needs to know who the player is, and the player needs to know what the game is, this is in fact a 'no problem'. Normal. In some projects it is normal to have a unified record (Registry) of some of the object types in the game - for example, the "game" could be a "Singleton" - in this case the player does not need a "self.game" - will always have a "game" global available where it can call methods. Similarly, the "player", if it is a single object (without simultaneous multi-players, etc...) could be a global resource, finished in the appropriate methods of the game, at the end of the game, phase change, etc....

Keeping them as references in the reciprocal instances is plus ordered and organized that the global resources - therefore it has no problem at all - and it facilitates rightly multiplayers, demo modes, computer-controlled player (which can be another instance of player, be in the same "game" but elsewhere on the map, for example).

To keep the answer complete, there is a "beautiful" and independent form of programming language, to avoid cross references, if you prefer: it is in all the player’s methods (that need it), you pass the game instance as parameter. I mean, instead of self.player.update(), the signature of the "update" method of Player would be def update(self, game): and your calls would be self.player.update(self). I’ve never done so - but it’s perfectly feasible.

So in fact, it’s only the practical question of avoiding hypothetical memory or resource leaks, which is solvable with the weakref I mentioned above. In Python you can do this:

from weakref import proxy

class Game:
    def __init__(self):
        self.player = Player(self)
    def redraw(self):
        print(self.player)
class Player:
    def __init__(self, game):
        self.game = proxy(game)
    def update(self):
        try:
            self.game.redraw()
        except ReferenceError:
            # self.game foi apagado
            pass

And testing at the terminal:

In [25]: g = Game()

In [26]: p = g.player

In [27]: p.update()
<__main__.Player object at 0x7fc3a856a438>

In [28]: del g

In [29]: p.update()

In [30]: 

If you choose to do this, it is worth reading the documentation of the weakref module - there are more practical ways than try...except ReferenceError: to use them, for example dictionaries and lists where items are automatically deleted if the instance they point to no longer exists.

In time, to give a real example of how these "cross-references" are used fairly in the context of games: I really like using the library Pygame - Among its few advantages is to have a simple api for 2D drawings and real-time control of keyboard and mouse events, and not to be a framework: the developer is responsible for the entire game flow. In Pygame the question of references to objects in the game is treated with what he calls "groups of sprites" - a game object (has to be a specific subclass), has a "Kill" method, and may be in one or several "groups". These groups work as sets (sets) in Python - for example, I could have a group with all the objects moving in the game - be it shots, ships, the player, etc.. and a group with a single object (the single instance of Player) - So the player is in several groups - and the simplicity of the thing is: you call the method kill player, and himself removes from all groups (so it will not be called in the next update of "tudp what moves"). Note that for this there is implicitly the "reciprocal reference": that is, the object "knows" in which groups it is. And everything works beautifully, without side effects, and transparently for the final programmer (so much so that I don’t even know if pygame uses weakrefs in its pygame.sprite.Group or not.)

  • 1

    It took a load off my consciousness haha. Hopefully one day I can help with quality content as well as Oce helped me. Thank you!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.