7.2. 赋值语句

赋值语句用于将名称(重)绑定到特定值,以及修改属性或可变对象的成员项:

  1. assignment_stmt ::= (target_list "=")+ (starred_expression | yield_expression)
  2. target_list ::= target ("," target)* [","]
  3. target ::= identifier
  4. | "(" [target_list] ")"
  5. | "[" [target_list] "]"
  6. | attributeref
  7. | subscription
  8. | slicing
  9. | "*" target

(请参阅 原型 一节了解 属性引用, 抽取切片 的句法定义。)

赋值语句会对指定的表达式列表进行求值(注意这可能为单一表达式或是由逗号分隔的列表,后者将产生一个元组)并将单一结果对象从左至右逐个赋值给目标列表。

赋值是根据目标(列表)的格式递归地定义的。 当目标为一个可变对象(属性引用、抽取或切片)的组成部分时,该可变对象必须最终执行赋值并决定其有效性,如果赋值操作不可接受也可能引发异常。 各种类型可用的规则和引发的异常通过对象类型的定义给出(参见 标准类型层级结构 一节)。

对象赋值的目标对象可以包含于圆括号或方括号内,具体操作按以下方式递归地定义。

  • 如果目标列表为后面不带逗号、可以包含于圆括号内的单一目标,则将对象赋值给该目标。

  • 否则:该对象必须为具有与目标列表相同项数的可迭代对象,这些项将按从左至右的顺序被赋值给对应的目标。

    • 如果目标列表包含一个带有星号前缀的目标,这称为“加星”目标:则该对象至少必须为与目标列表项数减一相同项数的可迭代对象。 该可迭代对象前面的项将按从左至右的顺序被赋值给加星目标之前的目标。 该可迭代对象末尾的项将被赋值给加星目标之后的目标。 然后该可迭代对象中剩余项的列表将被赋值给加星目标(该列表可以为空)。

    • 否则:该对象必须为具有与目标列表相同项数的可迭代对象,这些项将按从左至右的顺序被赋值给对应的目标。

对象赋值给单个目标的操作按以下方式递归地定义。

  • 如果目标为标识符(名称):

    • 如果该名称未出现于当前代码块的 globalnonlocal 语句中:该名称将被绑定到当前局部命名空间的对象。

    • 否则:该名称将被分别绑定到全局命名空间或由 nonlocal 所确定的外层命名空间的对象。

  1. 如果该名称已经被绑定则将被重新绑定。 这可能导致之前被绑定到该名称的对象的引用计数变为零,造成该对象进入释放过程并调用其析构器(如果存在)。
  • 如果该对象为属性引用:引用中的原型表达式会被求值。 它应该产生一个具有可赋值属性的对象;否则将引发 TypeError。 该对象会被要求将可赋值对象赋值给指定的属性;如果它无法执行赋值,则会引发异常 (通常应为 AttributeError 但并不强制要求)。

    注意:如果该对象为类实例并且属性引用在赋值运算符的两侧都出现,则右侧表达式 a.x 可以访问实例属性或(如果实例属性不存在)类属性。 左侧目标 a.x 将总是设定为实例属性,并在必要时创建该实例属性。 因此 a.x 的两次出现不一定指向相同的属性:如果右侧表达式指向一个类属性,则左侧会创建一个新的实例属性作为赋值的目标:

    1. class Cls:
    2. x = 3 # class variable
    3. inst = Cls()
    4. inst.x = inst.x + 1 # writes inst.x as 4 leaving Cls.x as 3

    此描述不一定作用于描述器属性,例如通过 property() 创建的特征属性。

  • 如果目标为一个抽取项:引用中的原型表达式会被求值。 它应当产生一个可变序列对象(例如列表)或一个映射对象(例如字典)。 接下来,该抽取表达式会被求值。

    如果原型为一个可变序列对象(例如列表),抽取应产生一个整数。 如其为负值,则再加上序列长度。 结果值必须为一个小于序列长度的非负整数,序列将把被赋值对象赋值给该整数指定索引号的项。 如果索引超出范围,将会引发 IndexError (给被抽取序列赋值不能向列表添加新项)。

    如果原型为一个映射对象(例如字典),抽取必须具有与该映射的键类型相兼容的类型,然后映射中会创建一个将抽取映射到被赋值对象的键/值对。 这可以是替换一个现有键/值对并保持相同键值,也可以是插入一个新键/值对(如果具有相同值的键不存在)。

    对于用户定义对象,会调用 __setitem__() 方法并附带适当的参数。

  • 如果目标为一个切片:引用中的原型表达式会被求值。 它应当产生一个可变序列对象(例如列表)。 被赋值对象应当是一个相同类型的序列对象。 接下来,下界与上界表达式如果存在的话将被求值;默认值分别为零和序列长度。 上下边界值应当为整数。 如果某一边界为负值,则会加上序列长度。 求出的边界会被裁剪至介于零和序列长度的开区间中。 最后,将要求序列对象以被赋值序列的项替换该切片。 切片的长度可能与被赋值序列的长度不同,这会在目标序列允许的情况下改变目标序列的长度。

CPython implementation detail: 在当前实现中,目标的句法被当作与表达式的句法相同,无效的句法会在代码生成阶段被拒绝,导致不太详细的错误信息。

虽然赋值的定义意味着左手边与右手边的重叠是“同时”进行的(例如 a, b = b, a 会交换两个变量的值),但在赋值给变量的多项集 之内 的重叠是从左至右进行的,这有时会令人混淆。 例如,以下程序将会打印出 [0, 2]:

  1. x = [0, 1]
  2. i = 0
  3. i, x[i] = 1, 2 # i is updated, then x[i] is updated
  4. print(x)

参见

PEP 3132 - 扩展的可迭代对象拆包

*target 特性的规范说明。