【问题标题】:Type hints: solve circular dependency [duplicate]类型提示:解决循环依赖
【发布时间】:2018-08-24 14:22:20
【问题描述】:

以下产生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)

【问题讨论】:

  • 写一个只实现服务器的第三个类,用客户端扩展它。

标签: python type-hinting python-typing


【解决方案1】:

如果您使用的是 Python 3.7+,请在另一个答案中使用 from __future__ import annotations as mentioned。但是,如果由于操作系统限制(如 Cygwin 截至 2019 年 6 月 3 日)还不能使用 3.7,则可以使用 Forward References 模块来满足这些类型的前向/循环依赖问题。

请原谅这个人为的例子,但这应该说明这种方法的有用性。

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

【讨论】:

  • QQ:上面用pyre测试过吗?这种方法更适合我尝试做的事情,但是,pyre 似乎很困惑
  • @urban - 从未使用过 pyre,只是第一次阅读。似乎是一个有趣的项目。我试试看。
  • @urban - 我确实查看了 pyre 工具并发现了与使用类型库有关的几个问题。 pyre 似乎正在逐案处理问题。任何想编写一个简单的测试用例并在 pyre github 页面上提出问题的人都可能会得到修复。对我来说,我找到了摆脱 Python 3.6 的方法,因此现在可以使用 future 中的注释库。祝你好运。
  • 这个答案中建议的方法是无效的,如果你try to type-check this code,mypy 会报一大堆错误。 T_Server 和 Server 或 T_Client 和 Client 之间没有实际联系。
  • @Monica - 感谢您提供信息。你可能是对的,但这确实让我摆脱了束缚。从那以后,我想出了如何将我的所有系统都安装到 Python 3.7+,因此我永远不会使用或推荐这种方法,除非有些人可能会陷入类似情况,他们不需要在工作流程中进行静态类型分析。跨度>
【解决方案2】:

您可以通过对尚未定义的Client 类使用字符串 名称来使用forward reference:

class Server():
    def register_client(self, client: 'Client')
        pass

As of Python 3.7,您还可以通过在模块顶部添加以下__future__ 导入来推迟所有运行时解析注释:

from __future__ import annotations

此时注释被存储为表达式的抽象语法树的字符串表示;您可以使用typing.get_type_hints() 来解决这些问题(并解决上面使用的前向引用)。

详情请见PEP 563 -- Postponed Evaluation of Annotations;此行为将是 Python 4.0 中的默认行为。

【讨论】:

  • 请注意,从 Python 3.7 开始,不再需要在此处使用字符串前向引用 -- 相反,将 from __future__ import annotations 导入添加到 delay evaluation of annotations。一旦Python 4.0 滚动,此行为显然会默认启用。
  • @MartijnPieters 我试图确认无论类名或字符串文字如何,这对 Python 都没有关系,因为它不会改变性能。因此,类型提示实际上是针对 3rd 方工具来获得最大利益和解决方案的,对吗? (忽略自我记录的代码片段)
  • @pasta_sauce:正确。基本上,类型提示会在加载模块所需的时间上增加非常少的时间。使用from __future__ import annotations 编译器开关,时间仍然进一步减少。
  • 虽然我非常喜欢from __future__ import annotations 解决方案,但由于某种原因它对我不起作用:NameError: name 'TypeName' is not defined。 Python版本是3.7。知道为什么它可能不起作用吗?
  • 为了详细说明我的观点,如果您没有就地定义注释类型而是创建类型别名,则此解决方案不起作用:MyType1 = typing.Union[str, MyType2] MyType2 = typing.Mapping[str, MyType1] 在这种情况下,您仍然会得到NameError
猜你喜欢
  • 1970-01-01
  • 2020-11-12
  • 1970-01-01
  • 2013-04-23
  • 1970-01-01
相关资源
最近更新 更多