簡體   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