7. 简单语句¶
简单语句包含在单行逻辑中。多条简单语句可以在一行上用分号隔开。简单语句的语法如下:
simple_stmt ::=expression_stmt
|assert_stmt
|assignment_stmt
|augmented_assignment_stmt
|annotated_assignment_stmt
|pass_stmt
|del_stmt
|return_stmt
|yield_stmt
|raise_stmt
|break_stmt
|continue_stmt
|import_stmt
|future_stmt
|global_stmt
|nonlocal_stmt
|type_stmt
7.1. 表达式语句¶
表达式语句用于(主要是在交互模式下)计算和写入值,或者(通常)调用过程(返回无意义结果的函数;在 Python 中,过程返回 None
值)。表达式语句的其他用法是允许的,偶尔也有用。表达式语句的语法如下:
expression_stmt ::= starred_expression
表达式语句会计算表达式列表(可以是单个表达式或用逗号分隔的列表,后者会生成元组)。
在交互模式下,如果值不是 None
,它将使用内置的 repr()
函数转换为字符串,生成的字符串将单独写入标准输出(除非结果是 None
,因此过程调用不会导致任何输出)。
7.2. 赋值语句¶
赋值语句用于将名称(重新)绑定到值,以及修改可变对象的属性或项目
assignment_stmt ::= (target_list
"=")+ (starred_expression
|yield_expression
) target_list ::=target
(","target
)* [","] target ::=identifier
| "(" [target_list
] ")" | "[" [target_list
] "]" |attributeref
|subscription
|slicing
| "*"target
(有关 attributeref、subscription 和 slicing 的语法定义,请参见第 Primaries 节)。
赋值语句会计算表达式列表(请记住,这可以是单个表达式或用逗号分隔的列表,后者会生成元组),并将单个结果对象从左到右分配给每个目标列表。
赋值的定义是递归的,取决于目标(列表)的形式。当目标是可变对象的一部分(属性引用、下标或切片)时,可变对象必须最终执行赋值并决定其有效性,如果赋值不可接受,则可能会引发异常。各种类型遵守的规则以及引发的异常在对象类型的定义中给出(参见部分 标准类型层次结构)。
将对象赋值给目标列表,可选地用括号或方括号括起来,递归定义如下。
如果目标列表是一个没有尾随逗号的单个目标,可选地用括号括起来,则将对象赋值给该目标。
否则
如果目标列表包含一个以星号为前缀的目标,称为“星号”目标:对象必须是一个可迭代对象,其项目数至少与目标列表中的目标数减 1 相同。可迭代对象的第一个项目从左到右赋值给星号目标之前的目标。可迭代对象的最后项目赋值给星号目标之后的目标。然后将可迭代对象中剩余项目的列表赋值给星号目标(列表可以为空)。
否则:对象必须是一个可迭代对象,其项目数与目标列表中的目标数相同,并且项目从左到右赋值给相应的目标。
将对象赋值给单个目标的递归定义如下。
如果目标是标识符(名称)
否则:名称在全局命名空间或由
nonlocal
确定的外部命名空间中绑定到该对象。
如果名称已经绑定,则重新绑定名称。这可能会导致先前绑定到名称的对象的引用计数达到零,从而导致对象被释放,其析构函数(如果有)被调用。
如果目标是属性引用:引用中的主要表达式被求值。它应该产生一个具有可赋值属性的对象;如果不是这样,则会引发
TypeError
。然后要求该对象将赋值对象赋值给给定的属性;如果它不能执行赋值,则会引发异常(通常但不一定是AttributeError
)。注意:如果对象是类实例,并且属性引用出现在赋值运算符的两侧,则右侧表达式
a.x
可以访问实例属性或(如果不存在实例属性)类属性。左侧目标a.x
始终设置为实例属性,并在必要时创建它。因此,a.x
的两次出现不一定引用同一个属性:如果右侧表达式引用类属性,则左侧会创建一个新的实例属性作为赋值的目标class Cls: x = 3 # class variable inst = Cls() inst.x = inst.x + 1 # writes inst.x as 4 leaving Cls.x as 3
此描述不一定适用于描述符属性,例如使用
property()
创建的属性。如果目标是下标:引用中的主要表达式被求值。它应该产生一个可变序列对象(如列表)或一个映射对象(如字典)。接下来,下标表达式被求值。
如果主对象是可变序列对象(如列表),则下标必须产生一个整数。如果它是负数,则序列的长度将加到它上面。结果值必须是一个非负整数,小于序列的长度,并且要求序列将赋值对象赋值给其具有该索引的项目。如果索引超出范围,则会引发
IndexError
(对下标序列的赋值不能向列表添加新项目)。如果主对象是映射对象(如字典),则下标必须具有与映射的键类型兼容的类型,然后要求映射创建一个键/值对,将下标映射到赋值对象。这可以将具有相同键值的现有键/值对替换,或者插入一个新的键/值对(如果不存在具有相同值的键)。
对于用户定义的对象,
__setitem__()
方法会使用适当的参数调用。如果目标是切片:引用中的主要表达式将被求值。它应该生成一个可变的序列对象(例如列表)。分配的对象应该是一个相同类型的序列对象。接下来,将求值下限和上限表达式,只要它们存在;默认值为零和序列的长度。边界应该求值为整数。如果任一边界为负数,则将序列的长度加到它上面。生成的边界被剪裁为介于零和序列的长度之间,包含两端。最后,要求序列对象用分配序列的项替换切片。切片的长度可能与分配序列的长度不同,因此会改变目标序列的长度,如果目标序列允许的话。
CPython 实现细节:在当前实现中,目标的语法被认为与表达式的语法相同,并且在代码生成阶段会拒绝无效的语法,从而导致不太详细的错误消息。
虽然赋值的定义暗示了左侧和右侧之间的重叠是“同时”的(例如 a, b = b, a
交换两个变量),但分配给变量的集合内部的重叠是从左到右发生的,有时会导致混淆。例如,以下程序打印 [0, 2]
x = [0, 1]
i = 0
i, x[i] = 1, 2 # i is updated, then x[i] is updated
print(x)
另请参阅
- PEP 3132 - 扩展可迭代解包
*target
功能的规范。
7.2.1. 增强赋值语句¶
增强赋值是在单个语句中组合二元运算和赋值语句
augmented_assignment_stmt ::=augtarget
augop
(expression_list
|yield_expression
) augtarget ::=identifier
|attributeref
|subscription
|slicing
augop ::= "+=" | "-=" | "*=" | "@=" | "/=" | "//=" | "%=" | "**=" | ">>=" | "<<=" | "&=" | "^=" | "|="
(有关最后三个符号的语法定义,请参见第 主要部分 节。)
增强赋值会求值目标(与普通赋值语句不同,它不能是解包)和表达式列表,对两个操作数执行特定于赋值类型的二元运算,并将结果分配给原始目标。目标只求值一次。
类似 x += 1
的增强赋值表达式可以改写为 x = x + 1
来实现类似但并不完全相同的效果。在增强版本中,x
只求值一次。此外,在可能的情况下,实际操作是在原地执行的,这意味着它不是创建一个新对象并将其分配给目标,而是修改旧对象。
与普通赋值不同,增强赋值在求值右侧之前求值左侧。例如,a[i] += f(x)
首先查找 a[i]
,然后它求值 f(x)
并执行加法,最后,它将结果写回 a[i]
。
除了分配给元组和在单个语句中分配给多个目标之外,增强赋值语句执行的赋值与普通赋值的处理方式相同。类似地,除了可能存在的原地行为之外,增强赋值执行的二元运算与普通二元运算相同。
对于作为属性引用的目标,与常规赋值一样,关于类和实例属性的警告 适用。
7.2.2. 带注释的赋值语句¶
注释 赋值是在单个语句中组合变量或属性注释和可选的赋值语句
annotated_assignment_stmt ::=augtarget
":"expression
["=" (starred_expression
|yield_expression
)]
与普通 赋值语句 的区别在于,只允许一个目标。
如果赋值目标由一个不在括号中的单个名称组成,则该目标被认为是“简单的”。对于简单的赋值目标,如果在类或模块范围内,则会求值注释并将其存储在特殊的类或模块属性 __annotations__
中,该属性是一个字典,它将变量名(如果为私有则为经过混淆的名称)映射到求值的注释。此属性是可写的,并且如果静态地找到注释,则会在类或模块主体执行开始时自动创建。
如果赋值目标不是简单的(属性、下标节点或带括号的名称),则如果在类或模块范围内,则会求值注释,但不会存储。
如果在函数作用域中对名称进行注释,则该名称对该作用域是本地的。注释永远不会被评估并存储在函数作用域中。
如果存在右侧,则带注释的赋值会在评估注释(如果适用)之前执行实际赋值。如果表达式目标没有右侧,则解释器会评估目标,除了最后一个 __setitem__()
或 __setattr__()
调用。
另请参阅
在版本 3.8 中更改: 现在带注释的赋值允许在右侧使用与常规赋值相同的表达式。以前,某些表达式(如未加括号的元组表达式)会导致语法错误。
7.3. The assert
statement¶
断言语句是将调试断言插入程序的一种便捷方式
assert_stmt ::= "assert"expression
[","expression
]
简单形式,assert expression
,等效于
if __debug__:
if not expression: raise AssertionError
扩展形式,assert expression1, expression2
,等效于
if __debug__:
if not expression1: raise AssertionError(expression2)
这些等效性假设 __debug__
和 AssertionError
指的是具有这些名称的内置变量。在当前实现中,内置变量 __debug__
在正常情况下为 True
,在请求优化时为 False
(命令行选项 -O
)。当前代码生成器在编译时请求优化时不会为断言语句生成任何代码。请注意,在错误消息中包含失败表达式的源代码是不必要的;它将作为堆栈跟踪的一部分显示。
对 __debug__
的赋值是非法的。内置变量的值在解释器启动时确定。
7.4. The pass
statement¶
pass_stmt ::= "pass"
pass
是一个空操作——当它被执行时,什么也不会发生。它在语法上需要语句但不需要执行任何代码时很有用,例如
def f(arg): pass # a function that does nothing (yet)
class C: pass # a class with no methods (yet)
7.5. The del
statement¶
del_stmt ::= "del" target_list
删除的递归定义与赋值的定义非常相似。这里没有完全详细地说明,只是一些提示。
目标列表的删除递归地删除每个目标,从左到右。
删除名称会从本地或全局命名空间中删除该名称的绑定,具体取决于名称是否出现在同一代码块中的 global
语句中。如果名称未绑定,则会引发 NameError
异常。
属性引用、下标和切片的删除将传递给所涉及的主要对象;切片的删除通常等效于分配正确类型的空切片(但即使这样也是由切片对象决定的)。
版本 3.2 中的变更: 以前,如果一个名称在嵌套块中作为自由变量出现,则从本地命名空间中删除该名称是非法的。
7.6. return
语句¶
return_stmt ::= "return" [expression_list
]
return
只能在语法上嵌套在函数定义中,不能在嵌套类定义中。
如果存在表达式列表,则对其进行求值,否则将用 None
替换。
return
使用表达式列表(或 None
)作为返回值离开当前函数调用。
当 return
将控制权从带有 finally
子句的 try
语句中传递出去时,该 finally
子句将在真正离开函数之前执行。
在生成器函数中,return
语句表示生成器已完成,并将导致 StopIteration
被引发。返回的值(如果有)将用作构造 StopIteration
的参数,并成为 StopIteration.value
属性。
在异步生成器函数中,空 return
语句表示异步生成器已完成,并将导致 StopAsyncIteration
被引发。非空的 return
语句在异步生成器函数中是语法错误。
7.7. yield
语句¶
yield_stmt ::= yield_expression
一个 yield
语句在语义上等效于 yield 表达式。yield 语句可用于省略在等效的 yield 表达式语句中否则所需的括号。例如,yield 语句
yield <expr>
yield from <expr>
等效于 yield 表达式语句
(yield <expr>)
(yield from <expr>)
Yield 表达式和语句仅在定义 生成器 函数时使用,并且仅在生成器函数的主体中使用。在函数定义中使用 yield 足以使该定义创建生成器函数而不是普通函数。
7.8. raise
语句¶
raise_stmt ::= "raise" [expression
["from"expression
]]
如果不存在表达式,则 raise
重新引发当前正在处理的异常,这也称为活动异常。如果没有活动异常,则会引发 RuntimeError
异常,表明这是一个错误。
否则,raise
将第一个表达式求值为异常对象。它必须是 BaseException
的子类或实例。如果它是一个类,则异常实例将在需要时通过使用无参数实例化该类来获得。
异常的类型是异常实例的类,值是实例本身。
当异常被抛出时,通常会自动创建一个回溯对象,并将其作为 __traceback__
属性附加到异常对象上。您可以使用 with_traceback()
异常方法(它返回相同的异常实例,其回溯设置为其参数)在一步骤中创建异常并设置自己的回溯,如下所示
raise Exception("foo occurred").with_traceback(tracebackobj)
from
子句用于异常链:如果给出,第二个 表达式 必须是另一个异常类或实例。如果第二个表达式是异常实例,它将作为 __cause__
属性(它是可写的)附加到抛出的异常。如果表达式是异常类,该类将被实例化,并且生成的异常实例将作为 __cause__
属性附加到抛出的异常。如果抛出的异常没有被处理,这两个异常都将被打印
>>> try:
... print(1 / 0)
... except Exception as exc:
... raise RuntimeError("Something bad happened") from exc
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
print(1 / 0)
~~^~~
ZeroDivisionError: division by zero
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
raise RuntimeError("Something bad happened") from exc
RuntimeError: Something bad happened
当异常正在被处理时,如果抛出了新的异常,类似的机制会隐式地起作用。当使用 except
或 finally
子句,或 with
语句时,异常可能会被处理。然后,之前的异常将作为新异常的 __context__
属性附加
>>> try:
... print(1 / 0)
... except:
... raise RuntimeError("Something bad happened")
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
print(1 / 0)
~~^~~
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
raise RuntimeError("Something bad happened")
RuntimeError: Something bad happened
通过在 from
子句中指定 None
,可以显式地抑制异常链
>>> try:
... print(1 / 0)
... except:
... raise RuntimeError("Something bad happened") from None
...
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened
有关异常的更多信息,请参见第 异常 节,有关处理异常的信息,请参见第 try 语句 节。
在 3.3 版中变更: None
现在在 raise X from Y
中被允许作为 Y
。
添加了 __suppress_context__
属性以抑制自动显示异常上下文。
在 3.11 版中变更: 如果在 except
子句中修改了活动异常的回溯,随后的 raise
语句将使用修改后的回溯重新抛出异常。以前,异常使用它被捕获时的回溯重新抛出。
7.9. The break
statement¶
break_stmt ::= "break"
break
只能在语法上嵌套在 for
或 while
循环中,但不能嵌套在该循环内的函数或类定义中。
它终止最近的封闭循环,如果循环有一个可选的 else
子句,则跳过该子句。
如果 for
循环被 break
终止,循环控制目标将保留其当前值。
当 break
将控制权从带有 finally
子句的 try
语句中传递出去时,该 finally
子句将在真正离开循环之前执行。
7.10. continue
语句¶
continue_stmt ::= "continue"
continue
只能在语法上嵌套在 for
或 while
循环中,但不能嵌套在该循环内的函数或类定义中。它将继续执行最近的封闭循环的下一个周期。
当 continue
将控制权从带有 finally
子句的 try
语句中传递出去时,该 finally
子句将在真正开始下一个循环周期之前执行。
7.11. import
语句¶
import_stmt ::= "import"module
["as"identifier
] (","module
["as"identifier
])* | "from"relative_module
"import"identifier
["as"identifier
] (","identifier
["as"identifier
])* | "from"relative_module
"import" "("identifier
["as"identifier
] (","identifier
["as"identifier
])* [","] ")" | "from"relative_module
"import" "*" module ::= (identifier
".")*identifier
relative_module ::= "."*module
| "."+
基本导入语句(没有 from
子句)在两个步骤中执行
查找模块,如果需要,加载并初始化它
在
import
语句发生的范围内的本地命名空间中定义一个或多个名称。
当语句包含多个子句(用逗号分隔)时,这两个步骤将分别对每个子句执行,就像这些子句被分成单独的导入语句一样。
第一步的详细信息,即查找和加载模块,在关于 导入系统 的部分中进行了更详细的描述,该部分还描述了可以导入的各种类型的包和模块,以及可以用来定制导入系统的所有钩子。请注意,此步骤中的失败可能表明模块无法找到,或者在初始化模块时发生了错误,包括执行模块的代码。
如果成功检索到请求的模块,它将在本地命名空间中以三种方式之一提供
如果模块名称后面跟着
as
,那么as
后面的名称将直接绑定到导入的模块。如果没有指定其他名称,并且要导入的模块是顶级模块,则模块的名称将在本地命名空间中绑定为对导入模块的引用
如果要导入的模块不是顶级模块,那么包含该模块的顶级包的名称将在本地命名空间中绑定为对顶级包的引用。必须使用导入模块的完全限定名称来访问导入模块,而不是直接访问它
from
形式使用稍微复杂的过程
查找
from
子句中指定的模块,如果需要,加载并初始化它;对于
import
子句中指定的每个标识符检查导入的模块是否具有该名称的属性
如果没有,尝试导入具有该名称的子模块,然后再次检查导入的模块以查找该属性
如果找不到属性,则会引发
ImportError
。否则,对该值的引用将存储在本地命名空间中,如果存在,则使用
as
子句中的名称,否则使用属性名称
示例
import foo # foo imported and bound locally
import foo.bar.baz # foo, foo.bar, and foo.bar.baz imported, foo bound locally
import foo.bar.baz as fbb # foo, foo.bar, and foo.bar.baz imported, foo.bar.baz bound as fbb
from foo.bar import baz # foo, foo.bar, and foo.bar.baz imported, foo.bar.baz bound as baz
from foo import attr # foo imported and foo.attr bound as attr
如果标识符列表被星号 ('*'
) 替换,则模块中定义的所有公共名称将在 import
语句出现的范围内的本地命名空间中绑定。
模块定义的公共名称通过检查模块的命名空间中是否存在名为 __all__
的变量来确定;如果定义了,它必须是一个字符串序列,这些字符串是该模块定义或导入的名称。在 __all__
中给出的名称都被认为是公共的,并且必须存在。如果未定义 __all__
,则公共名称集包括在模块命名空间中找到的所有不以下划线字符 ('_'
) 开头的名称。 __all__
应该包含整个公共 API。它的目的是避免意外导出不属于 API 的项目(例如在模块内部导入和使用的库模块)。
导入的通配符形式 - from module import *
- 仅允许在模块级别使用。尝试在类或函数定义中使用它将引发 SyntaxError
。
在指定要导入的模块时,您不必指定模块的绝对名称。当模块或包包含在另一个包中时,可以在同一个顶级包中进行相对导入,而无需提及包名称。通过在 from
之后指定的模块或包中使用前导点,您可以指定在不指定确切名称的情况下向上遍历当前包层次结构的高度。一个前导点表示模块进行导入所在的当前包。两个点表示向上一个包级别。三个点表示向上两个级别,等等。因此,如果您从 pkg
包中的模块执行 from . import mod
,那么您将最终导入 pkg.mod
。如果您从 pkg.subpkg1
中执行 from ..subpkg2 import mod
,您将导入 pkg.subpkg2.mod
。相对导入的规范包含在 包相对导入 部分中。
importlib.import_module()
用于支持动态确定要加载的模块的应用程序。
引发 审计事件 import
,参数为 module
、filename
、sys.path
、sys.meta_path
、sys.path_hooks
。
7.11.1. 未来语句¶
未来语句是向编译器发出的指令,指示特定模块应该使用在指定未来版本的 Python 中可用的语法或语义进行编译,其中该功能将成为标准。
未来语句旨在简化向未来版本的 Python 的迁移,这些版本对语言引入了不兼容的更改。它允许在功能成为标准的版本发布之前,在每个模块的基础上使用新功能。
future_stmt ::= "from" "__future__" "import"feature
["as"identifier
] (","feature
["as"identifier
])* | "from" "__future__" "import" "("feature
["as"identifier
] (","feature
["as"identifier
])* [","] ")" feature ::=identifier
未来语句必须出现在模块的顶部附近。未来语句之前只能出现以下几行
模块文档字符串(如果有),
注释、
空行,以及
其他未来语句。
唯一需要使用未来语句的功能是 annotations
(参见 PEP 563)。
Python 3 仍然识别未来语句启用的所有历史功能。该列表包括 absolute_import
、division
、generators
、generator_stop
、unicode_literals
、print_function
、nested_scopes
和 with_statement
。它们都是多余的,因为它们始终启用,仅出于向后兼容性而保留。
未来语句在编译时被识别并进行特殊处理:对核心结构语义的更改通常通过生成不同的代码来实现。甚至可能出现新功能引入新的不兼容语法(例如新的保留字)的情况,在这种情况下,编译器可能需要以不同的方式解析模块。此类决定不能推迟到运行时。
对于任何给定的版本,编译器都知道哪些功能名称已定义,如果未来语句包含它不知道的功能,则会引发编译时错误。
直接运行时语义与任何导入语句相同:有一个标准模块 __future__
(稍后描述),它将在执行未来语句时以通常的方式导入。
有趣的运行时语义取决于未来语句启用的特定功能。
请注意,语句本身没有什么特别之处
import __future__ [as name]
这不是未来语句;它是一个普通的导入语句,没有特殊的语义或语法限制。
在包含未来语句的模块 M
中发生的内置函数 exec()
和 compile()
的调用编译的代码将默认使用与未来语句相关的新的语法或语义。这可以通过 compile()
的可选参数来控制——有关详细信息,请参阅该函数的文档。
在交互式解释器提示符处键入的未来语句将对解释器会话的其余部分生效。如果解释器使用 -i
选项启动,传递要执行的脚本名称,并且脚本包含未来语句,它将在脚本执行后启动的交互式会话中生效。
另请参阅
- PEP 236 - 返回 __future__
__future__ 机制的原始提案。
7.12. global
语句¶
global_stmt ::= "global"identifier
(","identifier
)*
global
语句是一个声明,它对整个当前代码块有效。这意味着列出的标识符将被解释为全局变量。如果没有 global
,则不可能为全局变量赋值,尽管自由变量可以引用全局变量而无需声明为全局变量。
在 global
语句中列出的名称不能在同一代码块中在文本上位于该 global
语句之前。
在 global
语句中列出的名称不能定义为形式参数,也不能定义为 with
语句或 except
子句的目标,也不能定义为 for
目标列表、class
定义、函数定义、import
语句或变量注释。
CPython 实现细节:当前实现并未强制执行这些限制中的某些限制,但程序不应滥用这种自由,因为未来的实现可能会强制执行这些限制或静默地改变程序的含义。
程序员注意事项:global
是对解析器的指令。它仅适用于与 global
语句同时解析的代码。特别是,包含在提供给内置 exec()
函数的字符串或代码对象中的 global
语句不会影响包含函数调用的代码块,并且包含在 such a string 中的代码不受包含函数调用的代码中的 global
语句的影响。 eval()
和 compile()
函数也是如此。
7.13. The nonlocal
statement¶
nonlocal_stmt ::= "nonlocal"identifier
(","identifier
)*
当函数或类的定义嵌套(包含)在其他函数的定义中时,它的非局部作用域是封闭函数的局部作用域。 nonlocal
语句导致列出的标识符引用以前在非局部作用域中绑定的名称。它允许封装的代码重新绑定这些非局部标识符。如果一个名称在多个非局部作用域中绑定,则使用最近的绑定。如果一个名称未在任何非局部作用域中绑定,或者没有非局部作用域,则会引发 SyntaxError
。
nonlocal 语句适用于函数或类主体的所有作用域。如果在作用域中非局部声明之前使用或分配了变量,则会引发 SyntaxError
。
7.14. The type
statement¶
type_stmt ::= 'type'identifier
[type_params
] "="expression
The type
statement declares a type alias, which is an instance of typing.TypeAliasType
.
For example, the following statement creates a type alias
type Point = tuple[float, float]
这段代码大致等同于
annotation-def VALUE_OF_Point():
return tuple[float, float]
Point = typing.TypeAliasType("Point", VALUE_OF_Point())
annotation-def
表示一个 注解作用域,它的行为与函数类似,但有一些细微的差别。
类型别名的值在注解作用域中进行评估。它在创建类型别名时不会进行评估,而是在通过类型别名的 __value__
属性访问该值时才进行评估(参见 延迟评估)。这使得类型别名可以引用尚未定义的名称。
通过在名称后面添加 类型参数列表,可以使类型别名成为泛型。有关更多信息,请参见 泛型类型别名。
type
是一个 软关键字。
在 3.12 版本中添加。
另请参阅
- PEP 695 - 类型参数语法
引入了
type
语句和泛型类和函数的语法。