Python 类中的类型暗示
如何执行Python 类中的类型暗示,python,Python,如何执行node:node?因为当我运行它时,它说,没有定义名称“Node” 我是否应该删除:节点,并在函数中对其进行实例检查? 但是,我如何访问节点的属性(我希望它是节点类的实例) 顺便说一句,我不知道如何在Python中实现类型转换。类型检查中的“self”引用通常使用字符串完成: class Node: def append_child(self, node: Node): if node != None: self.first_child = nod
node:node
?因为当我运行它时,它说,没有定义名称“Node”
我是否应该删除:节点
,并在函数中对其进行实例检查?
但是,我如何访问节点的属性(我希望它是节点
类的实例)
顺便说一句,我不知道如何在Python中实现类型转换。类型检查中的“self”引用通常使用字符串完成:
class Node:
def append_child(self, node: Node):
if node != None:
self.first_child = node
self.child_nodes += [node]
PEP-0484一节对此进行了说明
请注意,这不会进行任何类型检查或转换。这是python(通常)完全忽略的类型提示1。但是,第三方工具(例如mypy)使用类型提示对代码进行静态分析,并可能在运行前生成错误
此外,从python3.7开始,您可以使用来自未来导入注释的(在python4.0中,这将是默认值)将所有类型提示隐式转换为模块内的字符串
1提示是可内省的——因此,如果您真的愿意,您可以使用它们来使用装饰器或类似工具构建某种运行时检查器,但python在默认情况下不会这样做。如果您只想得到问题的答案,请阅读。
mgilson的回答很好地解释了如何克服Python的这一限制。但我认为,很好地理解为什么这不起作用也很重要,所以我将提供这一解释
Python与其他语言略有不同。在Python中,实际上没有“声明”这样的东西。就Python而言,代码就是代码。导入模块时,Python会创建一个新的名称空间(全局变量可以存在的地方),然后从上到下执行模块的每一行def foo(args):code
只是一个复合语句,它将一组源代码捆绑到一个函数中,并将该函数绑定到名称foo
。类似地,class Bar(base):code
创建一个类,立即执行所有代码(在一个单独的名称空间内,该名称空间包含可能由代码创建的任何类级变量,特别是包括使用def
创建的方法),然后将该类绑定到名称Bar
。它必须立即执行代码,因为所有方法都需要立即创建。因为代码是在名称绑定之前执行的,所以不能引用代码顶层的类。但是,在方法内部引用类是完全正确的,因为在调用该方法之前,该代码不会运行
(您可能想知道为什么我们不能先绑定名称,然后再执行代码。事实证明,由于Python实现类的方式,在创建类对象之前,您必须知道前面存在哪些方法。可以创建一个空类,然后将所有方法一次绑定一个th属性赋值(实际上,您可以手动执行此操作,方法是编写类Bar:pass
,然后执行def method1():…;Bar.method1=method1
等等),但这将导致更复杂的实现,并且更难概念化,因此Python不会执行此操作。)
要用代码进行总结:
class Node:
def append_child(self, node: 'Node'):
if node != None:
self.first_child = node
self.child_nodes += [node]
Python3.7和Python4.03.10及以后版本
PEP 563引入了延迟评估,以字符串形式存储在\uuuuuuu注释中。用户可以通过以下指令启用此功能:
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.
这样就可以编写:
from __future__ import annotations
从Python3.10(计划发布2021-10-04)开始,这种行为将是默认的
<强>编辑2020-11-15:最初它被宣布为在Python 4中强制启动,但现在它出现了,这使我感到惊讶,因为它似乎违反了《代码》中的承诺:“YuxFuturux”,直到Python 4,这种向后兼容性才会被打破。也许开发者认为3.10比4是4,或者他们改变了主意。另请参见。
在Python>3.7中,您可以使用dataclass
。您还可以注释dataclass
在这个特定的示例中,节点
引用自身,如果您运行它,您将得到
class C:
a: C
def foo(self, b: C):
...
要克服此错误,您必须包括:
NameError: name 'Node' is not defined
它必须是模块中的第一行。在Python 4.0及更高版本中,您不必包含注释
from __future__ import 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
谢谢!老兄…从这种语言中可以学到很多东西。即使是其他语言中最简单的通用概念在Python arrrgg中也有其独特的方式。我同意。从这种语言中可以学到很多东西。我对Python有着复杂的感受。很棒的社区支持,广泛的库生态系统。但是,由于打字太差,这种语言被点燃了“神奇”的故事快捷方式。我也猜了很多,但这是一种黑客行为还是有意义?到目前为止,这是Python 3.9中的默认设置,对吗?@lcnittl似乎是这样,这让我感到惊讶,因为它似乎违反了\uuuu future\uuuuu
中的承诺,即它在Python 4.0之前不会成为默认设置,因为它破坏了向后兼容性。@lcnittl明白了,啊,该死,搞混了。谢谢你的澄清和跟进
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)