繁体   English   中英

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM