Python 中无需赋值的类型提示

问题描述 投票:0回答:2

我的印象是Python中的

typing
模块主要是为了提高代码可读性和代码文档目的。

在尝试过它并阅读了有关该模块的内容后,我成功地对它感到困惑。

即使这两个变量未初始化,下面的代码也可以工作(就像您通常初始化它们一样,例如

a = "test"
)。

我只在上面添加了类型提示,一切看起来都很好。也就是说,我没有得到

NameError
,就像我的代码中只有
a
时那样
NameError: name 'a' is not defined

以这种方式(带有类型提示)声明变量是一种好的做法吗?为什么这个有效?

from typing import Any

test_var: int
a: Any

print('hi')

我期望

test_var: int
返回一个错误,指出
test_var
未启动,我必须执行类似
test_var: int = 0
之类的操作(或任何值)。是否因为我向其中添加了类型提示而将其设置为默认值?

python annotations type-hinting python-typing
2个回答
12
投票

当您考虑所涉及的名称空间时,这相当简单。当您实际尝试使用 NameError

 执行任何操作(例如将其传递给函数(如 
test_var))时,您会得到
print
,这一事实暗示了这一点。它告诉您口译员不知道您使用的name

变量赋值有什么作用?

当您第一次为模块的全局命名空间中的变量赋值时,会发生什么情况,它会被添加到该模块的 globals 字典中,键是变量名称,值是它的值价值。您可以通过调用该模块中内置的

globals
函数来查看该字典:

(我将在以下一些示例中使用

pprint
以使输出更易于阅读。)

from pprint import pprint

a = 1

pprint(globals())

输出看起来像这样:

{'__annotations__': {},
 ...
 '__name__': '__main__',
 ...
 'a': 1,
 ...}

注释有什么作用?

当您仔细查看该词典时,您会发现其中另一个有趣的键,即

__annotations__
。现在,它的值是一个空字典。但我打赌你已经可以猜到,如果我们用类型注释我们的变量,会发生什么:

from pprint import pprint

a: int = 1

pprint(globals())

输出:

{'__annotations__': {'a': <class 'int'>},
 ...
 'a': 1,
 ...}

当我们向变量添加类型提示(即注释)时,解释器会将该名称和类型添加到相关的

__annotations__
字典中(请参阅注释赋值文档);在本例中是我们的模块。顺便说一句,由于
__annotations__
字典位于我们的全局命名空间中,我们可以直接访问它:

a: int = 1

print("a" in globals())        # True
print("a" in __annotations__)  # True

可以在不分配的情况下进行注释吗?

最后,如果我们只是注释而不给变量赋值,会发生什么?

a: int

print("a" in globals())        # False
print("a" in __annotations__)  # True

这就是为什么我们会收到错误的解释,如果我们尝试例如在此示例中打印出

a
,但否则不会出现任何错误。该代码只是告诉解释器(以及任何静态类型检查器)有关注释的信息,但它没有分配任何值,因此不会在全局命名空间字典中创建条目。

如果您考虑一下,这是有道理的:在该命名空间中,应该设置为

a
的值是什么?它没有任何价值(甚至没有
None
NotImplemented
或类似的东西)。对于解释器来说,
a: int
行仅意味着在我们模块的
__annotations__
中创建一个条目,这是完全有效的。

注释的运行时含义

我还想强调这样一个事实,即正如一些人经常声称的那样,注释对于解释器以及运行时来说“并非毫无意义”。诚然,它“很少”使用,但正如我们刚刚在示例中看到的那样,您绝对可以在运行时使用注释。这是否有用显然取决于您。一些软件包,如 Pydantic 或标准库的 dataclasses 实际上严重

依赖于注释来实现其目的。
在我们的示例中__annotations__字典中设置的值

实际上是

int
类的引用。因此,如果我们愿意的话,我们绝对可以在运行时使用它: a: int a_type = __annotations__["a"] print(a_type is int) # True print(a_type("2")) # 2
您也可以在 

类命名空间
 中尝试这个概念(不仅仅是模块命名空间),但我将把它作为练习留给读者。

总而言之,对于要添加到任何名称空间的名称,必须为其分配一个值。 分配值并仅提供注释就可以在该命名空间的

__annotations__

中创建条目。

Python 不会自动初始化变量,因此该变量不会被设置为任何内容。 

a: int

1
投票


© www.soinside.com 2019 - 2024. All rights reserved.