Python那些让我疑惑许久的代码
疑惑代码1
a, b = a[b] = {}, 5
print(a) # {5: ({...}, 5)}
print(b) # 5
疑惑点
- a, b = a[b] = {}, 5的执行流程是怎样的?
- 为什么会出现{…}
- 结果是怎么来的
解决疑惑点
a, b = a[b] = {}, 5的执行流程是怎样的?
官方文档中关于赋值语句的形式:
(target_list "=")+ (expression_list | yield_expression)
个人翻译等式应该是这样:
目标列表=多个计算表达式列表
按照列表赋值的方式,则应该是表达式运算完后依次从左到右赋值给目标列表
现在知道了列表赋值从左到右,但是却不知道{},5是先赋值给a,b还是a[b]
我们可以通过下面这个案例来说明{},5到底先赋值给谁
lst=[]
x=1
lst=lst[len(lst):] = lst[len(lst):] = [x]
print(lst)
我们这里可以先假设lst=lst[len(lst):] = lst[len(lst):] = [x]是从右往左开始赋值的
但是真实输出的答案为:
所以我们在进行另一个假设:lst=lst[len(lst):] = lst[len(lst):] = [x]是从左往右开始赋值的
???为什么是[1,1,1],那[1,1,1,1]到底怎么来的,反复看上面的图,和赋值表达式,唯一能证实的就是
S=[x]
S1=S
S2=S
这样运算才能获取[1,1,1,1],从而证实了python中如果存在多个目标时,会先将运算表达式赋值给最左边的目标,然后得到的结果分别赋值给其他目标
a,b=c=d=1,2
print(a,b)
print(c)
print(d)
通过上面的分析我们可以得到 "a, b = a[b] = {}, 5的执行流程是怎样的?"的答案了
1.a={},b=5
2.a[b]=a,b
为什么会出现{…}
**现在我们已经知道a[b]=a,b
那为什么会出现{…},{…}这个又是什么东西?
可以简单看个小案例
lst=lst[0]=[0]
print(lst) //[[...]]
其实[[…]]指的是与 lst 引用了相同的对象。**及lst[0] is lst,当引用的相同的对象时,Python就会打印出[…]
由与a[b]=a,b,a[b]中的a和a,b中的a是相同的对象,所以则会导致出现{…},这种现象
结果是怎么来的
解决上面二个问题这个问题就很容易解决了
1.a,b={},5
2.a={},b=5
3.a[b]={},5
4.a[5]=({....},5)
疑惑代码2
a=[1,2,3]
a+=[4,5,6]
a=a+[7,8,9]
print(a) //[1, 2, 3, 4, 5, 6, 7, 8, 9]
疑惑点
- +和+=有什么区别
- 二者的效率一样吗
解决疑惑点
+和+=有什么区别
在python的内存机制中,当不可变对象改变了原有的值,其别名绑定到了新值上面,id肯定会改变
+和+=的区别可以从可变对象和不可变对象说起
对于+号操作,可变对象和不可变对象调用的都是__add__操作
对于+=号操作,可变对象调用__add__,不可变对象调用的是__iadd__(不可变对象没有__iadd__) __iadd__是原地修改
在列表中+号则是一个特例
因为python中列表使用+的时候,等价于list.extend(list2),所以列表的另一个操作数必须是列表
二者的效率一样吗
import timeit
print(timeit.timeit("a = a + b + c", setup="a='a'*10000;b='b'*10000;c='c'*10000", number=100))
print(timeit.timeit("a += b + c", setup="a='a'*10000;b='b'*10000;c='c'*10000", number=100))
可以看出+=的效率明显高于+,但是二者效率的比较还是存在质疑
我们先从其机械指令来看为什么?
import dis
def fun():
a = 1
b = 2
a = a + b
a += b
dis.dis(fun)
结果:
3 0 LOAD_CONST 1 (1)
2 STORE_FAST 0 (a)
4 4 LOAD_CONST 2 (2)
6 STORE_FAST 1 (b)
5 8 LOAD_FAST 0 (a)
10 LOAD_FAST 1 (b)
12 BINARY_ADD
14 STORE_FAST 0 (a)
6 16 LOAD_FAST 0 (a)
18 LOAD_FAST 1 (b)
20 INPLACE_ADD
22 STORE_FAST 0 (a)
24 LOAD_CONST 0 (None)
26 RETURN_VALUE
二者唯一区别在于BINARY_ADD与INPLACE_ADD,从现在看出+=的效率高一些只能说明INPLACE_ADD效率总是不低于BINARY_ADD,但是未必a+=b 比 a=a+b快
1.JIT影响,很可能二者最终是相同的机器指令,就是一模一样
2.例子中int是immutable, INPLACE_ADD未必真正意义的inplace,否则就破坏了int immutable原则,至少能确定不论哪种方式加,每加一次a的内存地址变一次,a新分配地址是整个过程最大的开销(加逻辑计算可以忽略),在大量执行的情况下,二者应该开销基本一致,都是a内存分配开销
而且作者在莫论坛看到
所以a+=b和a=a+b在效率方面可能还是存在可变因素