问题:没有循环导入的Python类型提示
我正试图将我的大班分成两部分;好吧,基本上是进入“主”类和具有其他功能的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
。
我使用的是Python 3.4,如果在那里有解决方案,我愿意移至3.5。
有什么办法可以将我的Class分成两个文件并保留所有“连接”,以便我的IDE仍然可以自动完成以及知道类型的所有其他优点。
回答 0
恐怕通常没有一种非常优雅的方式来处理导入周期。您的选择是重新设计代码以消除循环依赖性,或者如果不可行,请执行以下操作:
# 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):
...
该from __future__ import annotations
进口将使所有类型提示弦而跳过评估他们。这可以使我们的代码更符合人体工程学。
综上所述,与mypy一起使用mixins可能需要比您现在拥有的结构更多的结构。Mypy 建议一种基本上就是deceze
所描述的方法-创建一个ABC,您的类Main
和MyMixin
类都继承。如果您最终需要做一些类似的事情以使Pycharm的检查器满意,我不会感到惊讶。
回答 1
对于仅在导入类以进行类型检查时陷入困境的人们:您可能希望使用前向引用(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
回答 2
更大的问题是,您的类型一开始并不理智。MyMixin
进行硬编码的假设是将其混合到中Main
,而可以将其混合到任何其他数量的类中,在这种情况下,它可能会损坏。如果将mixin硬编码为混合到一个特定的类中,则不妨将方法直接写入该类中,而不用将它们分开。
要使用合理的输入方式正确执行此操作,MyMixin
应使用Python的说法对interface或abstract class 进行编码:
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')
回答 3
事实证明,我最初的尝试也非常接近解决方案。这是我目前正在使用的:
# main.py
import mymixin.py
class Main(object, MyMixin):
def func1(self, xxx):
...
# mymixin.py
if False:
from main import Main
class MyMixin(object):
def func2(self: 'Main', xxx): # <--- note the type hint
...
请注意,import inside if False
语句永远不会被导入(但IDE仍然知道它),并且将该Main
类用作字符串,因为在运行时不知道。
回答 4
我认为,完美的方法应该是将所有类和依赖项导入文件(如__init__.py
),然后再导入所有from __init__ import *
其他文件。
在这种情况下
- 避免对这些文件和类的多次引用,并且
- 也只需在其他每个文件中添加一行
- 第三个是知道您可能使用的所有类的pycharm。