请选择 进入手机版|继续访问电脑版
发新帖

Python 的赋值坑 , a=b=c=1???

[复制链接]
7978 8

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
今天回答了一个主题, 一不小心进入了一个坑, 耗费了好多时间终于弄懂了
我想要将 a,b,c 变量同时赋值 1, 我使用了
```
In [192]: a = b = c = 1

In [193]: a, b, c
Out[193]: (1, 1, 1)
```
很明显我赋值成功了,a、b、c 都是 1
但是这个下赋值顺序是怎么样的
首先让我们先猜测一下:
第一种方式:a,b,c 同时赋值 1,a=1, b=1, c=1
第二种方式:依次赋值 c=1, b=c, a=b
以上两种显然是很合理的
但是我们要验证一下:
于是我开始说到我遇到的坑(其实这个是在说明 list 的 append 添加方法为什么返回的是 None 出现的)
假如我们创建变量 L 赋值为空, 我们先 L 以切片的形式添加 x 值
```
In [200]: L = []

In [201]: x = 4

In [202]: L[len(L):] = [x]

In [203]: L
Out[203]: [4]
```
我已经添加成功
由于 Python 的引用原理,我们可以多次添加
于是我想多次添加:
```
In [206]: L = []

In [207]: L[len(L):]=L[len(L):] = [x]

In [208]: L
Out[208]: [4, 4]

```
如我所料的一样我成功添加了 2 个 4, 也是说我每次都能获得 4,并向 L 尾部相加, 可以确定是第一种方式, 因为第二种根本只能加进一个 4:
第一种:L[len(L):]= [x], L[len(L):]= [x]
第二种:L[len(L):]= [x],L[len(L):]= L[len(L):]

好下面重点来了,我然后重点来了, 我又写了一个语句
```
In [209]: L = []

In [210]: L = L[len(L):]=L[len(L):] = [x]

In [211]: L
Out[211]: [4, 4, 4, 4]
```
这个不对啊
L 不应该等于[4]或者[4, 4, 4]吗,为什么会预测出两个结果,这个是赋值顺序问题, 正序和逆序
第一种方式的两种顺序:
正序:L = [x], L[len(L):] = [x], L[len(L):] = [x]   L=[4,4,4]
逆序:L[len(L):] = [x],L[len(L):] = [x],L=[x]  L=[4]

但是为什么会出现[4, 4, 4,4]
唯一的解释便是第三种赋值方式:
L = [x]                  # L = [4]
L[len(L):]=L         # L=[4,4]
L[len(L):]=L         # L=[4, 4, 4, 4]
这样我们就完全解释了这种状况
所以针对 a=b=c=1 这种赋值方式,其实是 a=1, b=a, c=a

举报 使用道具

回复

精彩评论8

xiaofengchen  新手上路  发表于 2018-4-3 13:29:21 | 显示全部楼层
所以平时工作我们应该分开赋值,或者加括号?

举报 使用道具

回复
yianing  新手上路  发表于 2018-4-3 13:31:15 | 显示全部楼层
恭喜 python 获得了和 c++同等的让人懵逼的技能

举报 使用道具

回复
orangeade  新手上路  发表于 2018-4-3 13:38:14 | 显示全部楼层
用 id(a) 命令看一下

举报 使用道具

回复
dickmrbean  新手上路  发表于 2018-4-3 14:24:41 | 显示全部楼层
In [1]: import dis

In [2]: dis.dis("a=b=c=1")
  1           0 LOAD_CONST               0 (1)
              2 DUP_TOP
              4 STORE_NAME               0 (a)
              6 DUP_TOP
              8 STORE_NAME               1 (b)
             10 STORE_NAME               2 (c)
             12 LOAD_CONST               1 (None)
             14 RETURN_VALUE

举报 使用道具

回复
tkmiles  新手上路  发表于 2018-4-3 14:39:33 | 显示全部楼层
1. 首先, 你发现没有, 就算你不先赋值 L, 也可以直接运行语句 L = L[len(L):]=L[len(L):] = [x]!!! 这说明了, python 是先赋值 L, 剩下的就很好理解了

2. 先赋值 L, 有 L=[4], 然后后一个 len 赋值, 有 L[len(L):]=L, 就是 L[1:] = L, 也就是 L[1:] = [4], 所以 L=[4, 4]

3. 然后第一个 len 赋值, 有 L[len[L]:] = L, L[2:] = L, L[2:]=[4, 4], 所以 L=[4, 4, 4, 4]

4. 看一下 dis

In [3]: dis.dis("L = L[len(L):]=L[len(L):] = [x]")
1           0 LOAD_NAME                0 (x)
              2 BUILD_LIST               1
              4 DUP_TOP
              6 STORE_NAME               1 (L)
              8 DUP_TOP
             10 LOAD_NAME                1 (L)
             12 LOAD_NAME                2 (len)
             14 LOAD_NAME                1 (L)
             16 CALL_FUNCTION            1
             18 LOAD_CONST               0 (None)
             20 BUILD_SLICE              2
             22 STORE_SUBSCR
             24 LOAD_NAME                1 (L)
             26 LOAD_NAME                2 (len)
             28 LOAD_NAME                1 (L)
             30 CALL_FUNCTION            1
             32 LOAD_CONST               0 (None)
             34 BUILD_SLICE              2
             36 STORE_SUBSCR
             38 LOAD_CONST               0 (None)
             40 RETURN_VALUE

发现确实是先 BUILD_LIST, 然后 STORE_NAME 到 L 上, 然后后面的 STORE_SUBSCR 得去 debug 一下 python 的 C 代码了, 我 debug 下来发现, STORE_SUBSCR 赋值的时候, 赋值的对象确实是 L 自己, 也就是说 L[len(L):] = L, 这是因为之前赋值了 L = [4]之后, python 就直接用 L 作为等号右边的符号了.

举报 使用道具

回复
ggarlic  新手上路  发表于 2018-4-3 14:51:41 | 显示全部楼层
恩,python 的赋值运算符是右结合的

举报 使用道具

回复
sapp  新手上路  发表于 2018-4-3 15:06:02 | 显示全部楼层
js 还有个更坑的,a = { b : 1}, a.c = a = { d: 1} // a.c undefined,这毛病一直到 es6 的 let 才给解决。

举报 使用道具

回复
kinghl  新手上路  发表于 2018-4-3 15:11:10 | 显示全部楼层
好好的 python,非要被你这样用。

举报 使用道具

回复
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表