【问题标题】:type hinting within a class [duplicate]类中的类型提示[重复]
【发布时间】:2017-04-29 08:04:36
【问题描述】:
class Node:
    def append_child(self, node: Node):
       if node != None:
        self.first_child = node
    self.child_nodes += [node]

node: Node 怎么办?因为当我运行它时,它会显示name 'Node' is not defined

我应该只删除: Node 并在函数内进行实例检查吗? 但是我怎么能访问node 的属性(我希望它是Node 类的实例)?

我不知道如何在 Python 中实现类型转换,顺便说一句。

【问题讨论】:

    标签: python


    【解决方案1】:

    类型检查中的“self”引用通常使用字符串来完成:

    class Node:
        def append_child(self, node: 'Node'):
           if node != None:
            self.first_child = node
        self.child_nodes += [node]
    

    这在 PEP-0484 的 "Forward references" 部分有所描述。

    请注意,这不会进行任何类型检查或强制转换。这是一个类型提示,python(通常)完全忽略1。但是,第三方工具(例如 mypy)使用类型提示对您的代码进行静态分析,并可能在运行前生成错误。

    此外,从 python3.7 开始,您可以隐式使用 from __future__ import annotations 将模块内的所有类型提示转换为字符串(在 python4.0 中,这将是默认)。

    1提示可自省的——所以如果你真的想的话,你可以使用它们来构建某种运行时检查器,使用装饰器或类似的东西,但是python默认不这样做。

    【讨论】:

    • 谢谢!伙计...从这种语言中可以学到很多东西。即使是来自其他语言的最简单的常见概念,在 Python arrrgg 中也有其独特的方式......
    • 我同意。从这种语言中可以学到很多东西。我对 Python 的感觉很复杂。强大的社区支持,广泛的图书馆生态系统。然而,这种糟糕的打字方式使得该语言充斥着“神奇”的快捷方式。
    • 我已经猜到了很多,但这是一种黑客行为还是有意义?
    【解决方案2】:

    Python 3.7 和 Python 4.03.10 以上

    PEP 563 引入了延迟评估,以字符串形式存储在 __annotations__ 中。用户可以通过__future__ 指令启用此功能:

    from __future__ import annotations
    

    这样就可以写了:

    class C:
        a: C
        def foo(self, b: C):
            ...
    

    从 Python 3.10(计划于 2021 年 10 月 4 日发布)开始,此行为将是默认行为。

    编辑 2020-11-15:最初宣布从 Python 4.0 开始强制使用,但现在出现 this will be default already in Python 3.10,即 expected 2021-10-04。这让我感到惊讶,因为它似乎违反了__future__ 中的承诺,即这种向后兼容性在 Python 4.0 之前不会被破坏。也许开发人员认为 3.10 是 4.0,或者他们改变了主意。另请参阅Why did __future__ MandatoryRelease for annotations change between 3.7 and 3.8?

    【讨论】:

    【解决方案3】:

    在 Python > 3.7 中,您可以使用 dataclass。也可以注解dataclass

    在这个特定的例子中,Node 引用了它自己,如果你运行它,你会得到

    NameError: name 'Node' is not defined
    

    要克服此错误,您必须包括:

    from __future__ import annotations
    

    它必须是模块中的第一行。在 Python 4.0 及更高版本中,您不必包含 annotations

    from __future__ import annotations
    from dataclasses import dataclass
    
    @dataclass
    class Node:
        value: int
        left: Node
        right: Node
    
        @property
        def is_leaf(self) -> bool:
            """Check if node is a leaf"""
            return not self.left and not self.right
    

    例子:

    node5 = Node(5, None, None)
    node25 = Node(25, None, None)
    node40 = Node(40, None, None)
    node10 = Node(10, None, None)
    
    # balanced tree
    node30 = Node(30, node25, node40)
    root = Node(20, node10, node30)
    
    # unbalanced tree
    node30 = Node(30, node5, node40)
    root = Node(20, node10, node30)
    

    【讨论】:

      【解决方案4】:

      如果您只想知道问题的答案,请阅读mgilson's answer

      mgilson 的回答很好地解释了您应该如何解决 Python 的这一限制。但我认为了解为什么这不起作用也很重要,所以我将提供这个解释。

      Python 与其他语言略有不同。在 Python 中,真的没有“声明”这样的东西。就 Python 而言,代码就是代码。当你导入一个模块时,Python 会创建一个新的命名空间(全局变量可以存在的地方),然后从上到下执行模块的每一行。 def foo(args): code 只是一个复合语句,它将一堆源代码捆绑到一个函数中,并将该函数绑定到名称 foo。同样,class Bar(bases): code 创建一个类,立即执行所有代码(在一个单独的命名空间中,该命名空间包含可能由代码创建的任何类级变量,特别是包括使用 def 创建的方法),然后将该类绑定到名称Bar。它必须立即执行代码,因为所有方法都需要立即创建。因为代码在名称绑定之前执行,所以您不能引用代码顶层的类。但是,在方法中引用类是完全可以的,因为该代码在方法被调用之前不会运行。

      (你可能想知道为什么我们不能先绑定名称然后执行代码。事实证明,由于 Python 实现类的方式,你必须先知道哪些方法存在,然后才能甚至创建类对象。可以创建一个空类,然后通过属性分配一次将所有方法绑定到它(实际上,您可以手动执行此操作,通过编写 class Bar: pass 然后执行 @ 987654328@ 等),但这会导致实现更复杂,并且更难以概念化,因此 Python 不会这样做。)

      用代码总结一下:

      class C:
          C  # NameError: C doesn't exist yet.
          def method(self):
              return C  # This is fine.  By the time the method gets called, C will exist.
      C  # This is fine; the class has been created by the time we hit this line.
      

      【讨论】:

        猜你喜欢
        • 2016-01-27
        • 1970-01-01
        • 2021-09-13
        • 2020-01-26
        • 2017-01-03
        • 2020-07-24
        • 2020-11-21
        • 1970-01-01
        相关资源
        最近更新 更多