skc*_*pto 7 python annotations type-hinting python-typing
我的印象是typing
Python 中的模块主要是为了提高代码可读性和代码文档目的。
在玩过它并阅读了有关该模块的内容之后,我设法让自己对它感到困惑。
即使这两个变量未初始化(就像您通常初始化它们一样,例如a = "test"
),下面的代码也可以工作。
我只在上面添加了类型提示,一切看起来都很好。也就是说,我没有得到 a ,就像我在代码中NameError
得到的那样a
NameError: name 'a' is not defined
以这种方式声明变量(带有类型提示)是一种好的做法吗?为什么这有效?
from typing import Any
test_var: int
a: Any
print('hi')
Run Code Online (Sandbox Code Playgroud)
我期望test_var: int
返回一个错误,指出该错误test_var
尚未启动,并且我必须执行类似的操作test_var: int = 0
(或任何值)。是否因为我向其中添加了类型提示而将其设置为默认值?
Dan*_*erg 11
当您考虑所涉及的名称空间时,这相当简单。NameError
当您实际尝试使用 执行任何操作test_var
(例如将其传递给函数(如))时,您会得到 , ,这一事实暗示了这一点print
。它告诉您口译员不知道您使用的名称。
当您第一次为模块的全局命名空间中的变量分配值时,会发生什么情况,它会被添加到该模块的全局字典中,键是变量名称,值是它的值。您可以通过调用该模块中的内置globals
函数来查看该字典:
from pprint import pprint
a = 1
pprint(globals())
Run Code Online (Sandbox Code Playgroud)
输出看起来像这样:
{'__annotations__': {},
...
'__name__': '__main__',
...
'a': 1,
...}
Run Code Online (Sandbox Code Playgroud)
当您仔细查看该字典时,您会发现其中另一个有趣的键,即__annotations__
。现在,它的值是一个空字典。但我打赌你已经可以猜到,如果我们用类型注释我们的变量,会发生什么:
{'__annotations__': {},
...
'__name__': '__main__',
...
'a': 1,
...}
Run Code Online (Sandbox Code Playgroud)
输出:
{'__annotations__': {'a': <class 'int'>},
...
'a': 1,
...}
Run Code Online (Sandbox Code Playgroud)
当我们向变量添加类型提示(即注释__annotations__
)时,解释器会将该名称和类型添加到相关字典中(请参阅文档);在本例中是我们的模块。顺便说一句,由于__annotations__
字典位于我们的全局命名空间中,我们可以直接访问它:
from pprint import pprint
a: int = 1
pprint(globals())
Run Code Online (Sandbox Code Playgroud)
最后,如果我们只是注释而不给变量赋值,会发生什么?
{'__annotations__': {'a': <class 'int'>},
...
'a': 1,
...}
Run Code Online (Sandbox Code Playgroud)
这就是为什么我们会得到一个错误的解释,如果我们尝试a
在这个例子中打印出来,但否则不会得到任何错误。该代码只是告诉解释器(以及任何静态类型检查器)有关注释的信息,但它没有分配任何值,因此无法在全局命名空间字典中创建条目。
如果您考虑一下,这是有道理的:应该将什么设置为该名称空间中的值a
?它没有任何价值(甚至没有None
任何NotImplemented
价值)。对于解释器来说,该行仅意味着在我们的模块a: int
中创建一个条目,这是完全有效的。__annotations__
我还想强调这样一个事实,即注释对于解释器和运行时来说并不是毫无意义的,正如一些人经常声称的那样。诚然,它很少被使用,但正如我们在示例中看到的,您绝对可以在运行时使用注释。这是否有用显然取决于您。一些包(例如Pydantic或标准库)dataclasses
实际上严重依赖注释来实现其目的。
__annotations__
在我们的示例中,字典中设置的值实际上是对该类的引用int
。因此,如果我们愿意的话,我们绝对可以在运行时使用它:
a: int = 1
print("a" in globals()) # True
print("a" in __annotations__) # True
Run Code Online (Sandbox Code Playgroud)
您也可以在类名称空间中使用这个概念(不仅仅是模块名称空间),但我将把它作为练习留给读者。
总而言之,要将名称添加到任何命名空间,必须为其分配一个值。不分配值而仅提供注释完全可以在该命名空间的__annotations__
.
归档时间: |
|
查看次数: |
2622 次 |
最近记录: |