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