当前位置: 首页 > 知识库问答 >
问题:

如何避免python中指针属性的类型暗示导致的循环依赖

慕学海
2023-03-14

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

首先是人。派克

from typing import List

from .pet import Pet


class Person:
    def __init__(self, name: str):
        self.name = name
        self.pets: List[Pet] = []
    
    def adopt_a_pet(self, pet_name: str):
        self.pets.append(Pet(pet_name, self))

然后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的定义保存在同一个文件中

e、 g.只要有:

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

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

还有别的办法吗?一个允许我

  • 将类拆分为不同的文件

或者:有没有很好的理由去遵循我已经列出的解决方案之一?

共有3个答案

澹台星光
2023-03-14

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

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)))

我使用类似于这里描述的技术解决了这个问题

现在,helper.py的更新内容如下:

# helper.py
import logging

def log(exception_obj: 'BaseException'):
    logging.error('Exception of type {} occurred'.format(type(exception_obj)))

请注意在exception_obj参数的类型注释中添加的引号。这帮助我安全地删除了导致循环依赖的导入语句。

注意:如果您使用的是IDE(比如PyCharm),您仍然可能会收到导入类的建议,IDE提示的类型将无法按预期工作。但是代码运行时没有任何问题。当您希望对代码进行注释以供其他开发人员理解时,这将非常有用。

申屠森
2023-03-14

在进一步学习之后,我意识到有一种正确的方法可以做到这一点:继承:

首先,我定义了Person,没有[宠物]或OP中的方法。然后我定义了宠物,拥有类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的方法现在都应该在PetOwner中定义,并且Pet中使用的所有方法/个人属性都需要亲自定义。如果需要在Pet中使用仅存在于PetOwner中的方法/属性,则应定义Pet的新子类,例如OwnedPet。

当然,如果这个名字让我不安,我可以从Person和PetOwner分别改为BasePerson和Person或类似的名字。

金理
2023-03-14

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

import typing

if typing.TYPE_CHECKING:
    from .person import Person


class Pet:
    def __init__(self, name: str, owner: 'Person'):
        self.name = name
        self.owner = owner

这里描述了第二种解决方案,它需要Python

from __future__ import annotations  # <-- Additional import.
import typing

if typing.TYPE_CHECKING:
    from .person import Person


class Pet:
    def __init__(self, name: str, owner: Person):  # <-- No more quotes.
        self.name = name
        self.owner = owner

从Python3.10开始,不再需要导入\uuuuu future\uuuu

 类似资料:
  • 问题内容: 我知道python中的循环导入问题已经出现过很多次了,我已经阅读了这些讨论。在这些讨论中反复提出的意见是,循环导入表明设计不良,应重新组织代码以避免循环导入。 有人可以告诉我在这种情况下如何避免循环导入吗?:我有两个类,并且我希望每个类都有一个构造函数(方法),该构造函数接受另一个类的实例并返回该类的实例。 更具体地说,一类是可变的,一类是不变的。哈希,比较等需要不可变的类。可变类也需

  • 问题内容: 我在MySQL中使用触发器存在一些问题。 假设我们有2个表: 表A 表B 和2个触发器: TriggerA:在TableA上删除并更新TableB时触发 TriggerB:在TableB上删除并在TableA中删除时触发 问题是,当我删除TableB中的某些行时,TriggerB触发并删除TableA中的某些元素,然后TriggerA触发并尝试更新TableB。 它失败,因为Trigg

  • 问题内容: 我尝试使用MVP范例构建Webapp。因为我希望API干净并且使所有内容易于测试,所以我尝试通过Contructor Injection注入所有可能的内容。现在,我到了一个具有多个文本字段的视图。当数据库中有值时,演示者将填充这些文本字段,因此我的演示者需要视图的引用,但是vie显然也需要演示者的引用。CDI告诉我,由于循环依赖性,无法将Presenter注入视图。是否有可能避免通过s

  • 如果我有一个班级ProfessorDto和一个班级StudentDto,如果ProfessorDto有一个StudentDto列表,StudentDto有一个ProfessorDto类型的属性,我如何避免循环问题?我没有给出域类的代码,但假设它与Dto的代码相同。 我是Mapstruct的新手,将域bean转换为具有简单属性的Dto,如Long,String正在工作,但在我的示例中,关系OneTo

  • 问题内容: 假设我具有以下目录结构: 在软件包的中,将导入软件包。但是进口。 程序失败,表示尝试导入b时不存在。(它实际上不存在,因为我们正在导入它。)`c_file.pya.b.d `如何解决这个问题? 问题答案: 如果a取决于c,而c取决于a,那么它们实际上不是同一单位吗? 您应该真正检查一下为什么将a和c拆分为两个包,因为您应该将一些代码拆分为另一个包(以使它们都依赖于该新包,而不是彼此依赖

  • 问题内容: 我正试图将我的大班分成两部分;好吧,基本上是进入“主”类和具有其他功能的mixin的,就像这样: 文件: 文件: 现在,尽管这很好,但类型提示当然不起作用。我无法导入,因为会进行周期性导入,并且没有提示,我的编辑器(PyCharm)无法分辨出什么。 我正在使用Python 3.4,如果那里有解决方案,我愿意移至3.5。 有什么办法可以将我的班级分成两个文件并保留所有“连接”,以便我的I