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

没有循环导入的Python类型提示

曹焱
2023-03-14

我试图把我庞大的班级一分为二;好的,基本上分为“main”类和带有附加函数的mixin,如下所示:

main。py文件

import mymixin.py

class Main(object, MyMixin):
    def func1(self, xxx):
        ...

mymixin。py文件:

class MyMixin(object):
    def func2(self: Main, xxx):  # <--- note the type hint
        ...

现在,虽然这很好,但是MyMixin中的类型提示。func2当然不能工作。我无法导入main。py,因为我会得到一个循环导入,并且没有提示,我的编辑器(PyCharm)无法分辨什么是self

我使用的是Python3.4,但如果有解决方案,我愿意使用3.5。

有没有什么方法可以把我的类分成两个文件,并保留所有的“连接”,这样我的IDE仍然可以为我提供自动完成和所有其他知道类型的好东西?


共有3个答案

宋昕
2023-03-14

更大的问题是你的类型一开始就不健全MyMixin硬编码假设它将混合到Main中,而它可以混合到任何数量的其他类中,在这种情况下,它可能会中断。如果您的mixin被硬编码为混合到一个特定的类中,那么您也可以将方法直接写入该类中,而不是将它们分离出来。

为了正确地使用正常的类型来完成这一操作,应该针对接口或Python术语中的抽象类进行编码:

import abc


class MixinDependencyInterface(abc.ABC):
    @abc.abstractmethod
    def foo(self):
        pass


class MyMixin:
    def func2(self: MixinDependencyInterface, xxx):
        self.foo()  # ← mixin only depends on the interface


class Main(MixinDependencyInterface, MyMixin):
    def foo(self):
        print('bar')
章乐逸
2023-03-14

对于仅为类型检查而导入类时遇到循环导入困难的用户:您可能希望使用正向引用(PEP 484-类型提示):

当类型提示包含尚未定义的名称时,该定义可以表示为字符串文本,稍后进行解析。

因此,不是:

class Tree:
    def __init__(self, left: Tree, right: Tree):
        self.left = left
        self.right = right

你可以:

class Tree:
    def __init__(self, left: 'Tree', right: 'Tree'):
        self.left = left
        self.right = right
邢曦
2023-03-14

恐怕没有一个非常优雅的方法来处理一般的导入周期。您可以选择重新设计代码以删除循环依赖关系,或者如果不可行,则执行以下操作:

# some_file.py

from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from main import Main

class MyObject(object):
    def func2(self, some_param: 'Main'):
        ...

TYPE_CHECKING常量在运行时总是False,因此导入不会被计算,但是mypy(和其他类型检查工具)将计算该块的内容。

我们还需要将Main类型注释变成一个字符串,有效地向前声明它,因为Main符号在运行时不可用。

如果您使用的是Python 3.7,我们至少可以通过利用PEP 563来跳过必须提供显式字符串注释的问题:

# some_file.py

from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from main import Main

class MyObject(object):
    # Hooray, cleaner annotations!
    def func2(self, some_param: Main):
        ...

来自未来导入注释的导入将使所有类型提示成为字符串,并跳过对它们的求值。这有助于使我们的代码稍微更符合人体工程学。

尽管如此,与mypy一起使用混合可能需要比您目前拥有的更多的结构。Mypy推荐了一种基本上就是deceze所描述的方法创建一个ABC,您的MainMyMixin类都继承了这个ABC。如果你最终需要做类似的事情来让皮魅力的支票员开心,我不会感到惊讶。

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

  • 我有Python类,它们有彼此作为属性的实例 如果这些类是在同一个文件中定义的,那么这很好,但是在我的例子中,它们都非常大,我希望将它们移动到不同的文件中。但是,如果我这样做,我必须将导入和导入,这将导致循环导入错误。有人知道我可以使用哪种模式将和放在不同的文件中,保持类型暗示,并遇到循环导入错误吗?

  • 问题内容: 如果两个模块相互导入会怎样? 为了概括这个问题,Python中的循环导入又如何呢? 问题答案: 导入确实非常简单。只要记住以下几点: 和是可执行语句。它们在运行的程序到达该行时执行。 如果模块不在中,则导入将在中创建新的模块条目,然后在模块中执行代码。在执行完成之前,它不会将控制权返回给调用模块。 如果中确实存在一个模块,则无论导入是否完成执行,导入都会简单地返回该模块。这就是循环导入

  • 问题内容: 所以我得到这个错误 并且你可以看到我进一步使用了相同的import语句,并且可以正常工作吗?关于循环导入是否有一些不成文的规定?如何在调用堆栈的更下方使用同一类? 问题答案: 我认为jpmc26的答案虽然绝非错误,但在循环进口上却过于严格。如果正确设置它们,它们可以正常工作。 最简单的方法是使用语法,而不是。前者几乎总是可以工作,即使包括在内也能使我们重新获得利益。后者只有在中已经定义

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

  • 问题内容: 我真的没有得到这个,所以如果有人可以解释它是如何工作的,我将非常感激。我有两个应用程序,“帐户”和“主题” …这是我的设置列表: 在帐户中,我正在尝试执行以下操作: 在我的主题模型中: Django踢出以下错误: 这是某种循环进口问题吗?我尝试使用一个懒惰的引用,但这似乎也不起作用! 问题答案: 删除的导入,并将模型名称用作字符串。