如何避免python中类型提示指针属性引起的循环依赖

时间:2017-10-09 07:38:55

标签: python annotations circular-dependency type-hinting cross-reference

考虑两个模块(在同一个文件夹中):

首先是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'

使其发挥作用的一些方法:

  1. 将Person和Pet类的定义保存在同一个文件中。
  2. 取消pet.owner属性(这是一个方便的指针)
  3. 不要使用类型提示/注释,它会导致循环引用:
  4. e.g。只是:

    class Pet:
        def __init__(self, name: str, owner):
    

    到目前为止,我在列出的所有选项中都看到了一些缺点。

    还有其他方法吗? 允许我

    的一个
    • 将类拆分为不同的文件
    • 将类型注释与显示
    • 之类的指针结合使用

    或者:是否有充分的理由选择我已经列出的解决方案之一?

3 个答案:

答案 0 :(得分:0)

经过一番学习,我意识到有一种正确的方法:继承:

首先我定义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或类似的东西。

答案 1 :(得分:0)

由于类型注释,我有一个类似的循环依赖错误用例。考虑一下该项目的以下结构:

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提示的类型将无法正常工作。但是代码运行没有任何问题。当您希望对代码进行注释以供其他开发人员理解时,这将很有帮助。

答案 2 :(得分:0)

最近我遇到了类似的问题,并使用以下方法解决了该问题:

rstan_package_skeleton(path = 'BayesianAAA', stan_files = "Model_A.stan" )

但是,此方法可能需要python 3.7及更高版本。