Python 与 C 中每行的多个属性

Multiple attributions per line in Python versus C

尽我所能理解 python 的工作原理。我来自有点扎实的 C 背景。

正在尝试将以下行翻译成 C: p, q = q, p - x // y * q

我的问题是多个 assertions/attributions(请纠正我的术语)无法正常工作。

这行代码实际上是如何解包成类似 C 的语法的?

你可以用 C 做同样的事情:

temp = p - x / y * q; 
p = q; 
q = temp;

但是真正是右侧创建了一个元组,元组项从左到右构建:

t = (q, p - x // y * q)

然后元组在左侧解包:

p = t[0]
q = t[1]

这种使用元组进行本质上并行赋值的做法非常方便。而且我相信您会很快习惯的。

但是可能需要更长时间才能习惯的一件事是 Python 的数据模型,它的工作方式与 C 的数据模型截然不同。您可能会发现这篇文章很有帮助:Facts and myths about Python names and values,由 SO 资深人士 Ned Batchelder 撰写。

任何 assignment statement 的工作原理是首先计算右侧的表达式,然后将其分配给目标列表。1


在你的例子中,右边是一个 tuple display 有两个元素:

q, p - x // y * q

因此,Python 计算第一个元素 q,然后计算第二个元素 p - x // y * q,然后它从这两个值构建一个元组。

接下来,由于您的目标列表左侧有两个目标,Python 使用可迭代解包(在相同的元组和序列教程部分中描述)2 将该元组解压缩为两个单独的值,并为每个目标分配一个值。

所以,用 C 语言来说,大致是:

_tmp1 = q
_tmp2 = p - x // y * q
_tmp3 = (_tmp1, _tmp2)
p = _tmp3[0]
q = _tmp3[1]

事实上,真正的 Python 实现可能会优化 build-a-tuple/unpack-a-tuple 代码,就像您自己编写代码一样,所以您得到的更像是:

_tmp = p - x // y * q
p = q
q = _tmp

如果您习惯使用 C,您可能习惯于查看编译器生成的程序集(未禁用优化)来弄清楚某些东西的真正含义。您可以在 Python 中使用 dis 模块执行相同的操作,除了汇编语言用于 CPython 字节码而不是 x86_64 或 ARM9 或其他任何语言:

>>> import dis
>>> dis.dis('p, q = q, p - x // y * q')
  1           0 LOAD_NAME                0 (q)
              2 LOAD_NAME                1 (p)
              4 LOAD_NAME                2 (x)
              6 LOAD_NAME                3 (y)
              8 BINARY_FLOOR_DIVIDE
             10 LOAD_NAME                0 (q)
             12 BINARY_MULTIPLY
             14 BINARY_SUBTRACT
             16 ROT_TWO
             18 STORE_NAME               1 (p)
             20 STORE_NAME               0 (q)
             22 LOAD_CONST               0 (None)
             24 RETURN_VALUE

正如你所看到的,在 CPython 3.7 中,它实际上 确实 优化了元组,并且还优化了临时局部变量并仅存储中间值在堆栈上匿名值。


1.这有点过于简单化了,因为像 spam[eggs][0] = … 这样的目标实际上需要将 spam[eggs] 作为表达式求值,然后将 <the result of that>[0] 视为目标……但这与此处无关。

2。本教程称之为 "sequence unpacking",因为它还没有涉及 "iterable" 的概念。