我正在尝试制作一个基于文本的RPG。我有一些代码,比如:
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):
# ...
整个文件结构如下:
|__ backend
__init__.py
monsters.py
heroes.py
MainGame.py
假设我希望Monster
和Hero
访问彼此的Attack
和TakeDamage
函数,例如:
class Monster():
def __init__(self):
pass
def Attack(self, target):
# ...
def TakeDamage(self, amount, target:Hero):
damage = # damage calculation here
target.TakeDamage(damage)
我该怎么做?到目前为止,我已经尝试过:
- 在各自的文件中导入彼此(例如
from .monsters import Monster
)-这会导致读取ImportError: cannot import name 'Monster' from partially initialized module 'backend.monsters' (most likely due to a circular import)
时出错
一般来说,类没有方法;类的目的是定义数据类型。为了使用实例,您不需要在Python中看到该定义
考虑Monster
类中的代码:
def TakeDamage(self, amount, target:Hero):
damage = # damage calculation here
Hero.TakeDamage(damage)
(我暂时忽略了这里的逻辑可能没有那么大意义。)
编写:Hero
是一个提示——对阅读代码的人来说,可能对第三方工具来说;Python本身并不关心-target
将是Hero
类的实例。
我们想在该实例上调用一个方法,而不是在类上调用。类没有TakeDamage
方法;它只有一个TakeDamage
函数,当我们通过实例查找它时,该函数用于创建方法。
因此,代码应该而不是说Hero.TakeDamage(damage)
。应该是target.TakeDamage(damage)
,因为target
是我们将调用其方法的Hero
实例的名称。
为此,我们不需要Hero
类的定义。monsters.py
不应该import
做任何事情来实现此功能。
当代码正在运行时,在尝试方法调用的那一刻,Python将检查名为target
的东西是否具有TakeDamage
属性。当它没有找到一个直接附加到实例的函数时,它会在类中查找,并在该类中找到TakeDamage
函数。它将自动从中创建一个方法。然后,它将检查从这个过程中获得的TakeDamage
是否是可调用的(剧透:它是,因为它是一个方法),并调用它。
以下是我设计这款游戏的方法:
├── backend
│ ├── __init__.py
│ ├── character.py
│ ├── heros.py
│ └── monsters.py
└── main.py
character.py
是英雄和怪物之间的通用类。
# 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)
输出:
Hero(health=95)
Monster(health=99)
这里的关键是发生在attack
方法内部:它用amount调用target.take_damage()
。
注意,heros.py
不导入monsters.py
,反之亦然。这样做可能会导致循环引用,这很混乱。