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在效率方面可能还是存在可变因素

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值