python - 如何处理 Python 中不同模块的相关对象之间的循环引用?

为了提高我的(初学者)Python 技能,我开始了一个宠物项目,现在我遇到了循环导入问题。

宠物项目是一款类似口袋妖怪的小游戏,除其他外,还有一群带着武器的动物。 关系链:团队->动物->武器(一个团队由几只动物组成,每只动物都持有一件武器)。 为了避免类别过大,我决定将不同类别的动物和武器分布在两个文件中,并使用导入来相互访问。 来自 Java,我喜欢强类型变量、参数和参数。

精简一点,我的类 weapons.py 和 animals.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 编码风格(以及我对循环导入的理解)的建议

最佳答案

要了解如何构建代码,您可以从组合、聚合和关联的角度考虑。

What is the difference between association, aggregation and composition?

https://www.visual-paradigm.com/guide/uml-unified-modeling-language/uml-aggregation-vs-composition/

仍然有几种可能性,您需要决定哪一种是最重要的(所有者有武器,武器有所有者)。

假设每件武器一次只有一个所有者,您希望如何访问该武器?

owner.weapon -> 那么你就知道拥有者了

或者您可以保留对所有者的引用作为武器的属性:

weapon.owned_by -> 也许在这里使用 id 而不是对实际类的引用,这就是你当前的问题,对吗?

没有主人的武器存在吗?然后看Composition:

组合 表示 child 不能独立于 parent 而存在的关系。

组合示例: 房子( parent )和房间( child )。没有房子就没有房间。

非合成的示例: 汽车和轮胎。轮胎没有汽车。

关于为什么要更好地避免循环引用的一般主题:https://softwareengineering.stackexchange.com/questions/11856/whats-wrong-with-circular-references

你也可以尝试考虑依赖倒置(注入(inject)原则) (参见 here 或 here ). 我认为您已经在第一种方法中尝试过(将 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   

您可以阅读描述符 here , here 和更多的理论here .

https://stackoverflow.com/questions/61075991/

相关文章:

javascript - 如何在谷歌地图中心制作固定标记以使用react?

flutter - 实现 youtube 播放器时 Flutter App 出错

angular - 在 Angular 9 中,我应该使用什么来代替 @HostBinding ("

python-3.x - 如何在 python 中将 dict 列表转换为 json?

amazon-web-services - Glue Crawler 无法识别时间戳

javascript - 如何在 Javascript 中获取选定的 pdf 文本?

python - 在 Windows 10 上安装 weasyprint 的问题

pyspark - 实例化时出错 'org.apache.spark.sql.hive.HiveEx

java - Spring Data MongoDB 中的 INNER JOIN 集合

python - 自动完成什么都不做。怎么了?