Question

I'm working on a simple game in python (which I'm fairly new to as a language) using pygame and it seems as though python really hates circular dependencies (although I'm aware there are ways around this).

Generally for collision detection, I would have a callback function that is called, once a collision is detected, for each object involved in the collision. The problem is that in doing so, each object involved in the collision would need to know about the other in order to resolve the collision in the correct way, thus resulting in a circular dependency which I would prefer to avoid (see below).

Here is the Enemy.py module:

from Player include * #dependency on player

class Enemy():
    def handle_collision(other_object):
        if isinstance(other_object,Player) #this check requires the Enemy class to know about Player

Here is the Player.py module:

from enemy include * #dependency on enemy

class Player():
    def handle_collision(other_object):
        if isinstance(other_object,Wall): 
            #do what we need to do to resolve a wall collision
        elif isinstance(other_object,Enemy): #this check requires that we include the Enemy class
            #do what we need to do to resolve an enemy collision    

Any suggestions or insight into how this is generally handled would be great.

Was it helpful?

Solution

Cyclic dependencies like these, where the module player.py imports the module enemy.py and vice versa are indeed rather tricky in Python. But there are (at least) two ways to work around those:

First, you do not need to import a module to use classes from that module. Even without importing the enemy.py module, you can use an instance of the Enemy class, e.g. after it has been passed to the handle_collision method as a parameter by some other module. Then, you could work around your problem by checking e.g. for other_object.__class__.__name__ == 'Enemy'.

This works, but it is not very nice, and will cause problems with, e.g., subclasses of Enemy.

Second, you do not have to put each class in it's own module/file, as it is common in, e.g., Java. In Python, it is perfectly normal and good practice to put many related classes in one and the same module. (One of the reasons why cyclic dependencies between modules are discouraged in Python is because needing them is considered a sign of bad system design.)

So my advice would be to put all of your "entity" classes into the same module, e.g. entities.py

class Entity:
    def handle_collision(self, other_object):
        pass

class Player(Entity):
    def handle_collision(self, other_object):
        if isinstance(other_object, Enemy):
            # do stuff

class Enemy(Entity):
    def handle_collision(self, other_object):
        if isinstance(other_object, Player):
            # do other stuff

Note that circular imports are in fact possible in Python, but should be used with care. For more about this, you can take a look at these two related posts.

OTHER TIPS

You can generally eliminate circular dependency errors by moving the import from the module-level into a function or method where you actually need it.

In Player.py, try moving from enemy include * (which should be IMPORT, not INCLUDE) to be within your handle_collision method:

def handle_collision(other_object):
    from enemy import Enemy
    if isinstance(other_object,Wall): 
        #do what we need to do to resolve a wall collision
        pass
    elif isinstance(other_object,Enemy): 
        #this check requires that we include the Enemy class
        pass
        #do what we need to do to resolve an enemy collision    

Also you should try to avoid importing * as it makes it difficult to see where your various imported names come from.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top