提问人:Tamriel 提问时间:11/21/2015 最后编辑:wimTamriel 更新时间:7/3/2023 访问量:32328
类型提示:解决循环依赖
Type hints: solve circular dependency
问:
以下生成 .我该如何解决?NameError: name 'Client' is not defined
class Server:
def register_client(self, client: Client)
pass
class Client:
def __init__(self, server: Server):
server.register_client(self)
答:
150赞
Martijn Pieters
11/21/2015
#1
可以通过使用尚未定义的类的字符串名称来使用正向引用:Client
class Server:
def register_client(self, client: 'Client')
pass
从 Python 3.7 开始,您还可以通过在模块顶部添加以下导入来推迟注释的所有运行时解析:__future__
from __future__ import annotations
此时,注释将存储为表达式的抽象语法树的字符串表示形式;您可以使用 typing.get_type_hints()
来解析这些问题(并解析上面使用的正向引用)。
有关详细信息,请参见 PEP 563 -- 推迟的注释评估。
评论
13赞
Michael0x2a
7/28/2018
请注意,从 Python 3.7 开始,这里将不再需要使用字符串前向引用——而是添加导入以延迟注释的评估。一旦 Python 4.0 推出,此行为显然将默认启用。from __future__ import annotations
0赞
3/25/2019
@MartijnPieters 我试图确认,无论类名或字符串文字如何,这对 Python 来说都无关紧要,因为它不会改变性能。因此,类型提示实际上是针对第三方工具,以获得最大的收益和解决方案,对吗?(忽略自我记录的代码片段)
0赞
Martijn Pieters
3/25/2019
@pasta_sauce:正确。基本上,类型提示会给加载模块所需的时间增加非常少的时间。使用编译器开关,该时间进一步减少。from __future__ import annotations
1赞
Semisonic
3/28/2019
虽然我非常喜欢这个解决方案,但由于某种原因它对我不起作用:.Python 版本为 3.7。知道为什么它可能不起作用吗?from __future__ import annotations
NameError: name 'TypeName' is not defined
1赞
Semisonic
3/28/2019
为了阐述我的观点,如果您不就地定义注释类型,而是创建一个类型别名,则此解决方案不起作用: 在这种情况下,您仍然会得到一个MyType1 = typing.Union[str, MyType2] MyType2 = typing.Mapping[str, MyType1]
NameError
6赞
Timothy C. Quinn
6/6/2019
#2
如果您使用的是 Python 3.7+,请使用另一个答案中提到的。但是,如果由于操作系统限制(如截至 2019 年 6 月 3 日的 Cygwin)而无法使用 3.7,则可以使用正向引用模块来满足这些类型的正向/循环依赖问题。from __future__ import annotations
请原谅这个人为的例子,但这应该说明这种方法的有用性。
class Server():
clients: list = None
def __init__(self):
self.clients=[]
def register_client(self, client: 'Client') -> None:
self.clients.append(client)
print('Client `%s` registered with server' % client.name)
def print_clients(self) -> None:
for i, client in enumerate(self.clients):
print('client %i: %s' % (i, client.name))
@staticmethod
def build_clone(server: 'Server') -> 'Server':
svr_new: Server = Server()
for client in server.clients:
svr_new.register_client(client)
return svr_new
class Client():
name: str = None
def __init__(self, name: str, server: 'Server'):
self.name = name
server.register_client(self)
svr = Server()
cli = Client('foo', svr)
svr.print_clients()
svr_clone = Server.build_clone(svr)
svr_clone.print_clients()
评论
0赞
urban
6/18/2019
QQ:你用柴堆测试过以上吗?这种方法更适合我尝试做的事情,但是,柴堆似乎很困惑
0赞
Timothy C. Quinn
6/19/2019
@urban - 从未使用过柴堆,只是第一次阅读。似乎是一个有趣的项目。我会试一试。
1赞
Timothy C. Quinn
8/28/2019
@urban - 我确实审查了 pyre 工具,发现了与使用键入库相关的几个问题。似乎柴堆正在根据具体情况处理问题。任何想要编写一个简单的测试用例并提出 pyre github 页面问题的人,他们都可能会得到修复。对我来说,我已经找到了摆脱 Python 3.6 的方法,因此现在可以使用未来的注释库。祝你好运。
0赞
user2357112
8/20/2020
这个答案中建议的方法是无效的,如果你尝试对这段代码进行类型检查,mypy 会报告一大堆错误。和 、 或 和 之间没有实际的联系。T_Server
Server
T_Client
Client
0赞
Timothy C. Quinn
8/21/2020
@Monica - 感谢您提供的信息。你可能是对的,但这确实让我摆脱了束缚。从那以后,我弄清楚了如何让我的所有系统都达到 Python 3.7+,因此永远不会使用或推荐这种方法,除非有些人可能陷入类似的场景,他们不需要在工作流中进行静态类型分析。
评论