在我们使用Python
的过程中, 经常遇到赋值语句, 就像下面的那样:
a = 3b = 3
可能你会觉得我又要说什么变量赋值就是引用, 这么简单的知识就不讨论啦, 相信聪明的大家肯定都知道的, 我想讲的是链式赋值
先科普下什么是链式赋值
:
链式赋值: 同时对几个变量进行赋值
例如:
a = b = c = 3
好了, 现在正式进入正题:
>>> s = [1, 2, 3, 4, 5, 6]>>> i = 0>>> i = s[i] = 3
i 和 s 的值分别是什么? 可能大家一眼看下去, 就能得出答案:
i 的值: 3s 的值: [3, 2, 3, 4, 5, 6]
然而, 这个答案只是对了一半, 因为s的值错了! 有兴趣的朋友可以自行上机试下, 正确答案是:
i 的值: 3s 的值: [1, 2, 3, 3, 5, 6]
s的列表, 并没有像我们想象中的那样, 就i=0
位置上的元素, 变成3, 而是将i=3
位置的元素改成3了, 为什么会这样? 一起来解析下吧, 上dis
大杀器!
[root@iZ23pynfq19Z ~]# cat 2.py s = [1, 2, 3, 4, 5]i = 0g = i = s[i] = 3[root@iZ23pynfq19Z ~]# python -m dis 2.py 1 0 LOAD_CONST 0 (1) 3 LOAD_CONST 1 (2) 6 LOAD_CONST 2 (3) 9 LOAD_CONST 3 (4) 12 LOAD_CONST 4 (5) 15 BUILD_LIST 5 18 STORE_NAME 0 (s) 2 21 LOAD_CONST 5 (0) 24 STORE_NAME 1 (i) 3 27 LOAD_CONST 2 (3) 30 DUP_TOP 31 STORE_NAME 2 (g) 34 DUP_TOP 35 STORE_NAME 1 (i) 38 LOAD_NAME 0 (s) 41 LOAD_NAME 1 (i) 44 STORE_SUBSCR 45 LOAD_CONST 6 (None) 48 RETURN_VALUE
第一列的数字, 代表中间的字节码
是属于哪一行代码的.
LOAD_CONST
5个数字, 组成一个列表, 赋值给s,再取一个0, 赋值给i.接下来的就是我们关心的, 也是带给我们意外的代码. 第3行:
LOAD_CONST
取出常量3, 它并不是像上面执行STORE_NAME
, 而是采用DUP_TOP
, 这是什么鬼, 我们这要去看下这指令具体是干嘛的: //取自 python/ceval.cPyEval_EvalFrameEx(PyFrameObject *f, int throwflag){ ... (省略) TARGET_NOARG(DUP_TOP) { v = TOP(); // 复制运行栈帧的顶部值 Py_INCREF(v); // 增加引用计数 PUSH(v); // 再压入运行栈帧 FAST_DISPATCH(); } ... (省略)}
DUP_TOP
指令说白了, 就是将刚才LOAD_CONST
指令取出的常量3, 复制一份给v,然后再压回去运行栈帧, 这样就有两个3了, 为什么要这么做, 肯呢个大家已经猜到了, 不过我们还是得继续看具体是不是像我们想的那样, 继续看会字节码:
35 STORE_NAME 1 (i) 38 LOAD_NAME 0 (s) 41 LOAD_NAME 1 (i) 44 STORE_SUBSCR
果然不出我们所料, 开始将这些3通过STORE_NAME
赋值给i, 而对于s, 它反而是, 再一次LOAD_NAME
取出i的值, 此时i的值是3, 不是一开始的0了, 在通过STORE_SUBSCR
指令, 将刚才压入运行时时栈的3赋值给位置是3的元素, 具体的源码就不再看, 到这就够了.
[1, 2, 3, 3, 5, 6]
这跟我们想象中的链式赋值很不同, 我们以前总是觉得, 赋值要从右到左依次执行, 先执行 s[i] = 3
, 再执行 i=3
, 然而这些是类似c语言
这类支持表达式赋值才允许的. 在c语言
中, s = 3
表达式是有返回值的. 它会返回赋值的结果3, 所以在它们的链式赋值
中, 是将右边表达式的返回值, 再赋值给左边的, 例如:
a = s = 3等价于:a = (s = 3)也就是 s=3 返回3, 再赋值给a
而在python
是不支持这种表达式赋值的, 也就是表达式是没有返回值的, 如果硬要a = (s = 3)
只会触发SyntaxError: invalid syntax
感谢@Daetalus童鞋指出问题:
支持表达式赋值是Python语言的核心,比如a = b + 3。这里的 b + 3
就是表达式。如果不支持表达式赋值就没法继续下去了。a = (s = 3)出错的原因是因为s = 3是赋值语句,而不是表达式。
Python的表达式是由操作符连接而成的,但“=”在Python中并不是操作符(Operator),只是语法分隔符(Delimiters)。参见
欢迎各位大神指点交流,转载请注明来源: