[英]How to handle circular references between related objects from different modules in Python?
为了提高我的(初学者)Python 技能,我开始了一个宠物项目,现在我遇到了循环导入问题。
宠物项目是一款口袋妖怪式的小游戏,其中包括穿着武器的动物团队。 关系链:团队 -> 动物 -> 武器(一个团队由几只动物组成,每只动物持有一把武器)。 为了避免过大的类,我决定将非常不同的动物和武器类分布在两个文件中,并使用导入来相互访问。 来自 Java 我喜欢强类型变量、arguments 和参数。
所以剥离了一点,我的类武器.py 和动物.py 看起来像这样:
import weapons
class Animal():
def __init__(self, name: str, level: int):
self.name: str = name
self.level: int = int
self.weapon: Weapon or None = None
def equip(self, weapon: Weapon) -> None:
self.weapon = weapon
import animals
from abc import ABC
class Weapon(ABC):
def __init__(self, type: str, power_level: float):
self.type: str = type
self.power_level: float = power_level
self.wielder: Animal or None = None
def set_wielder(wielder: Animal) -> None:
self.wielder = wielder
因此,当我实例化动物时,我不希望它们立即使用武器,也不希望武器立即拥有所有者。 但是,虽然动物 -> 武器的关系在游戏中相当直接,但我也希望有一种方法可以从武器指向拥有它的动物。
上面的代码会导致循环导入问题。 当面临一个不同但相关的问题时,我发现了有趣的__future__
模块。 添加“ from __future__ import annotations
”解决了我的问题。
但是,虽然我对我的工作代码感到高兴,但我想知道我是否可以以更优雅的方式解决这个问题。 这是否是臭代码。 是否有不同的解决方案仍然允许我使用打字。 我很高兴有任何建议可以改进我的 Python 编码风格(以及我对循环导入的理解)
要了解如何构建代码,您可以从组合、聚合、关联方面进行思考。
https://www.visual-paradigm.com/guide/uml-unified-modeling-language/uml-aggregation-vs-composition/
仍然有几种可能性,您需要确定哪个是最重要的(拥有者有武器,武器有拥有者)。
假设每个武器一次只有一个拥有者,你想如何访问武器?
owner.weapon
-> 那么你就知道所有者了
或者您可以保留对所有者的引用作为武器的属性:
weapon.owned_by
-> 可能在这里使用id
而不是对实际 class 的引用,这就是您当前的问题,对吧?
没有所有者的武器是否存在? 然后看组成:
组合意味着孩子不能独立于父母而存在的关系。
组合示例:House(父)和 Room(子)。 没有房子就没有房间。
非组合示例:汽车和轮胎。 轮胎没有汽车。
关于为什么更好地避免循环引用的一般线程: https://softwareengineering.stackexchange.com/questions/11856/whats-wrong-with-circular-references
您还可以尝试考虑依赖倒置(注入原则) (参见此处或此处)。 我认为您已经在第一种方法中尝试过(将 Weapon 实例传递给 Animal)。 这个想法很好,但也许你需要在两者之间再增加一层。
另一件事,来自 Java,你习惯于 getter 和 setter。 这在 Python 中并不流行(但你可以做到)。
你的方法:
class Weapon(ABC):
def set_wielder(wielder: Animal) -> None:
self.wielder = wielder
更多 Pythonic,使用属性(“描述符”):
class Weapon(ABC):
def __init__(self):
# notice the underscore, it indicates "treat as non-public"
# but in Python there is no such thing
self._wielder = None
@property #this makes it work like a getter
def wielder(self) -> Animal: # not sure about the annotation syntax
return self._wielder
@wielder.setter
def wielder(wielder: Animal) -> None:
self._wielder = wielder
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.