Home > Enterprise >  How do I get functions from two different classes in two different files to communicate with each ot
How do I get functions from two different classes in two different files to communicate with each ot

Time:07-01

I'm trying to make a text-based RPG. I have some code like:

heroes.py:

class Hero():
   def __init__(self):
      pass
   def Attack(self, target):
      # ...
   def TakeDamage(self, amount):
      # ...

monsters.py:

class Monster():
   def __init__(self):
      pass
   def Attack(self, target):
      # ...
   def TakeDamage(self, amount):
      # ...

The whole file structure looks like this:

|__ backend
    __init__.py
    monsters.py
    heroes.py
MainGame.py

Let's say I want Monster and Hero to access each other's Attack and TakeDamage functions, for example:

class Monster():
   def __init__(self):
      pass
   def Attack(self, target):
      # ...
   def TakeDamage(self, amount, target:Hero):
      damage = # damage calculation here
      target.TakeDamage(damage)

How can I do this? So far I've tried:

  • Importing each other (e.g. from .monsters import Monster) in their respective files - this gives me an error reading ImportError: cannot import name 'Monster' from partially initialized module 'backend.monsters' (most likely due to a circular import).

CodePudding user response:

Broadly speaking, classes don't have methods; instances do. The purpose of a class is to define a data type. You don't need to see that definition, in Python, in order to use instances.

Consider the code in the Monster class:

   def TakeDamage(self, amount, target:Hero):
      damage = # damage calculation here
      Hero.TakeDamage(damage)

(I will ignore for the moment that the logic here probably doesn't make that much sense.)

Writing :Hero is a hint - to the person reading the code, and possibly to third-party tools; Python itself does not care - that target will be an instance of the Hero class.

We want to call a method on that instance, not on the class. The class doesn't have a TakeDamage method; it only has a TakeDamage function, which is used to create the method when we look it up via an instance.

Therefore, the code should not say Hero.TakeDamage(damage). It should say target.TakeDamage(damage), because target is the name of the Hero instance whose method we will call.

To do this, we do not require the definition of the Hero class. monsters.py should not import anything to make this work.

When the code is running, at the moment that the method call is attempted, Python will check whether the thing that is named target has a TakeDamage attribute. When it doesn't find one directly attached to the instance, it will look in the class, and find the TakeDamage function in that class. It will automatically create a method from that. Then it will check that the TakeDamage that it got from this process is callable (spoiler: it is, because it's a method), and call it.

CodePudding user response:

Here is a way for me to design this game:

├── backend
│   ├── __init__.py
│   ├── character.py
│   ├── heros.py
│   └── monsters.py
└── main.py

character.py is the common class between hero and monster.

# character.py
class Character:
    def __init__(self):
        self.health = 100

    def attack(self, target):
        target.take_damage(1)

    def take_damage(self, amount):
        self.health -= amount

    def __repr__(self):
        return (
            f"{self.__class__.__name__}("
            f"health={self.health!r}"
            f")"
        )
# heros.py
from .character import Character

class Hero(Character):
    pass
# monsters.py
from .character import Character

class Monster(Character):
    def attack(self, target):
        target.take_damage(5)
# main.py
from backend.heros import Hero
from backend.monsters import Monster

h = Hero()
m = Monster()

h.attack(m)
m.attack(h)

print(h)
print(m)

Output:

Hero(health=95)
Monster(health=99)

The key here is happening inside the attack method: it calls target.take_damage() with the amount.

Note that heros.py does not import monsters.py and vice versa. Doing so could result in circular reference, which is messy.

  • Related