考虑两个模块(在同一个文件夹中):
首先,person.py
from typing import List
from .pet import Pet
class Person:
def __init__(self, name: str):
self.name = name
self.pets = [] # type: List[Pet]
def adopt_a_pet(self, pet_name: str):
self.pets.append(Pet(pet_name))
然后pet.py
from .person import Person
class Pet:
def __init__(self, name: str, owner: Person):
self.name = name
self.owner = owner
由于循环依赖,上面的代码不起作用。你会收到一个错误:
ImportError: cannot import name 'Person'
一些使它工作的方法:
例如只有:
class Pet:
def __init__(self, name: str, owner):
我看到到目前为止我列出的所有选项都有一些缺点。
还有另外一种方法吗?一个允许我
或者:是否有充分的理由来遵循我已经列出的解决方案之一?
经过一番学习,我意识到有一种正确的方法可以做到这一点:继承:
首先我定义Person,没有[pets]或OP中的方法。然后我定义了Pets,拥有Person类的所有者。然后我定义
from typing import List
from .person import Person
from .pet import Pet
class PetOwner(Person):
def __init__(self, name: str):
super().__init__(name)
self.pets = [] # type: List[Pet]
def adopt_a_pet(self, pet_name: str):
self.pets.append(Pet(pet_name))
现在,Pet中需要引用Pet的所有方法都应该在PetOwner中定义,而Pet中使用的Person的所有方法/属性都需要在Person中定义。如果需要在Pet中使用仅存在于PetOwner中的方法/属性,则新的子类Pet,例如应该定义OwnedPet。
当然,如果命名困扰我,我可以从Person和PetOwner分别改为BasePerson和Person或类似的东西。
由于类型注释,我有一个类似循环依赖性错误的用例。考虑一下,项目的以下结构:
my_module
|- __init__.py (empty file)
|- exceptions.py
|- helper.py
内容:
# exceptions.py
from .helper import log
class BaseException(Exception):
def __init__(self):
log(self)
class CustomException(BaseException):
pass
# helper.py
import logging
from .exceptions import BaseException
def log(exception_obj: BaseException):
logging.error('Exception of type {} occurred'.format(type(exception_obj)))
我通过使用类似于描述here的技术解决了它
现在,helper.py
的更新内容如下所示:
# helper.py
import logging
def log(exception_obj: 'BaseException'):
logging.error('Exception of type {} occurred'.format(type(exception_obj)))
请注意exception_obj
参数的类型注释中添加的引号。这有助于我安全地删除导致循环依赖的import语句。
警告:如果您正在使用IDE(如PyCharm),您仍然可能会建议导入类,并且IDE的类型提示将无法按预期工作。但代码运行没有任何问题。当您希望保持代码注释以供其他开发人员理解时,这将非常有用。
我最近遇到了类似的问题,我使用以下方法解决了它:
from __future__ import annotations
import typing
if typing.TYPE_CHECKING:
from .person import Person
但是,此方法可能需要python 3.7及更高版本。