Swift51.com
麦子学院 头像
麦子学院  2017-05-13 13:40

python链式赋值有哪些注意事项?

回复:0  查看:2263  
在我们在python 开发 http://www.maiziedu.com/land/python/中,  经常遇到赋值语句 就像下面的那样 :
  a = 3b = 3
  可能你会觉得我又要说什么变量赋值就是引用这么简单的知识就不讨论啦 相信聪明的大家肯定都知道的 本文和大家分享的是 链式赋值相关内容,一起来看看吧,希望对大家学习和使用这部分内容有所帮助。
  先科普下什么是  链式赋值 :
  链式赋值同时对几个变量进行赋值
  例如:
  a = b = c = 3
  好了现在正式进入正题 :
  >>> s = [1, 2, 3, 4, 5, 6]>>> i = 0>>> i = s = 3
  和  的值分别是什么 可能大家一眼看下去 就能得出答案 :
   i  的值: 3
  的值 : [3, 2, 3, 4, 5, 6]
  然而这个答案只是对了一半 因为 s 的值错了 有兴趣的朋友可以自行上机试下 正确答案是 :
   i  的值: 3
  的值 : [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 = 0
  g = 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
  第一列的数字代表中间的  字节码  是属于哪一行代码的.
  第1~2 行简单解释下 :
  分别  LOAD_CONST 5 个数字 组成一个列表 赋值给 s, 再取一个 0,  赋值给 i. 接下来的就是我们关心的 也是带给我们意外的代码 .
  第3 :
   LOAD_CONST  取出常量3,  它并不是像上面执行  STORE_NAME ,  而是采用  DUP_TOP ,  这是什么鬼 我们这要去看下这指令具体是干嘛的 :
  // 取自  python/ceval.c
  PyEval_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 = 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
  希望大家以后在用到这种链式赋值时尽量避免这些问题哦
来源:SegmentFault