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

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

回复:0  查看:2931  
在我们在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  和  s  的值分别是什么 ?  可能大家一眼看下去 ,  就能得出答案 :
   i  的值: 3
  s  的值 : [3, 2, 3, 4, 5, 6]
  然而,  这个答案只是对了一半 ,  因为 s 的值错了 !  有兴趣的朋友可以自行上机试下 ,  正确答案是 :
   i  的值: 3
  s  的值 : [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