背景

写 Python 四年有余了,常见的坑和奇淫巧技也都知道一些。今天在 Python 连续赋值上遇到了一个新的知识点,学习记录一下。

Python 连续赋值容易导致错误的情况有两个知识点:

相同的引用

赋值的顺序(本次探讨的重点)

相同的引用

这个有点 Python 经验的同学都知道或者很好理解,对可变对象的赋值,其实赋的是可变对象的引用。

在下面的代码中,因为['hello']是一个列表,即可变对象,变量a和b都是这个同一个列表的引用。因此,对b修改时,a的值也变了,因为它们本来就指向同一个对象。

1

2

3

4

5

6

7

8s1 = s2 = 'hello'

s2 += 'world'

print(s1)

a = b = ['hello']

b.append('world')

print(a) # Output: ['hello', 'world']

print(id(a) == id(b)) # Output: True

赋值的顺序

通常我们并不是太关心在使用连续赋值过程中赋值的顺序,就像下面这两行代码,赋值的先后次序并不影响结果。

1

2s1 = s2 = 'hello'

a = b = ['hello']

但到底是:

是先把'hello'赋值给s2,然后把s2赋值给s1吗?

还是把'hello'赋值给s2,然后'hello'赋值给s1呢?

又或者是把'hello'赋值给s1,然后'hello'赋值给s2呢?

在下面这个例子中让我犯了难。1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22class :

def __init__(self, x):

self.val = x

self.next = None

def __repr__(self):

return '->'.join(map(str, ([self.val, self.next.val] if self.next else [self.val])))

def main():

a = ListNode(0)

a = a.next = ListNode(1)

print(a)

b = ListNode(0)

b.next = ListNode(1)

b = b.next

print(b)

if __name__ == '__main__':

main()

Output:

1->1

1

我本期望赋值操作可以从右向左进行赋值,让上面代码中的变量a和b有相同的结果。即:

1a = a.next = ListNode(1)

我期望上面的语句等同于

1

2a.next = ListNode(1)

a = a.next

事与愿违,解释器总是很忠实的按照原有的设计去执行代码。那么它到底是怎么做的呢?

我们可以import dis,把 python 代码反汇编来看一下。1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24import dis

class :

def __init__(self, x):

self.val = x

self.next = None

def __repr__(self):

return '->'.join(map(str, ([self.val, self.next.val] if self.next else [self.val])))

def main():

a = ListNode(0)

a = a.next = ListNode(1)

print(a)

b = ListNode(0)

b.next = ListNode(1)

b = b.next

print(b)

if __name__ == '__main__':

dis.dis(main)

下面看看 Line 14, 18, 19 翻译过来的汇编码:

汇编语言我并不了解,下面的注释是我根据命令名称的猜测,如有错误恳请指出。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23...

14 8 LOAD_GLOBAL 0 (ListNode) // 加载ListNode

10 LOAD_CONST 2 (1) // 加载常量 1

12 CALL_FUNCTION 1 // 调用函数(可能是ListNode的构造函数)

14 DUP_TOP

16 STORE_FAST 0 (a) // 赋值给变量 a

18 LOAD_FAST 0 (a) // 加载变量 a

20 STORE_ATTR 1 (next) // 赋值给属性 next

...

18 38 LOAD_GLOBAL 0 (ListNode) // 加载ListNode

40 LOAD_CONST 2 (1) // 加载常量 1

42 CALL_FUNCTION 1 // 调用函数(可能是ListNode的构造函数)

44 LOAD_FAST 1 (b) // 加载变量 b

46 STORE_ATTR 1 (next) // 赋值给属性 next

19 48 LOAD_FAST 1 (b) // 加载变量 b

50 LOAD_ATTR 1 (next) // 加载变量 b 的属性 next

52 STORE_FAST 1 (b) // 赋值给变量 b

...

由汇编码就很容易看出,在 Python 中类似a = b = 'hello'的连续赋值过程,实际上是将最右侧的常量或变量,对其左侧=前的各变量从左至右依次赋值。

即:1a = b = 'hello'

等同于1

2a = 'hello'

b = 'hello'

回到上面让我犯难的例子中:

1a = a.next = ListNode(1)

等同于

1

2

3

4_ = ListNode(1)

a = _

a.next = _

# 注意:这里我用_暂存了对象,整个过程中只创建了一个对象

而不是

1

2a.next = ListNode(1)

a = a.next

了解了原理,要想实现我期望的效果,其实只要调换一下顺序就行,像下面这样:1a.next = a = ListNode(1)

不过这样降低了可读性,不利于理解,最好还是不要这样写!

总结这种错误隐秘难调,最好不要使用连续赋值(Chained Assignment)。

对基础知识点要加强学习。

连续赋值在其他编程语言中似乎不是这样的,Python 是个特例,这点有待考证!!!

python可以连续赋值吗_Python连续赋值的两个要点相关推荐

  1. python 列表比较不同_python实现比较两段文本不同之处的方法

    本文实例讲述了python实现比较两段文本不同之处的方法.分享给大家供大家参考.具体实现方法如下: # find the difference between two texts # tested w ...

  2. python相似图片识别_Python+Opencv识别两张相似图片

    Python+Opencv识别两张相似图片 在网上看到python做图像识别的相关文章后,真心感觉python的功能实在太强大,因此将这些文章总结一下,建立一下自己的知识体系. 当然了,图像识别这个话 ...

  3. python基础案例教程_Python基础教程 两个经典案例:阶乘和幂

    6.6.1 两个经典案例:阶乘和幂 本节探讨两个经典的递归函数.首先,假设你要计算数字n的阶乘. n的阶乘为n × (n1) × (n 2) × - × 1,在数学领域的用途非常广泛.例如,计 ...

  4. python画图颜色填充_python画图的两种方法

    python如何画图?这里给大家介绍两款python绘图的库:turtle和Matplotlib. 相关推荐:<python视频> 1 安装turtle Python2安装命令:pip i ...

  5. python识别图片中数字_Python Opencv识别两张相似图片

    在网上看到python做图像识别的相关文章后,真心感觉python的功能实在太强大,因此将这些文章总结一下,建立一下自己的知识体系. 当然了,图像识别这个话题作为计算机科学的一个分支,不可能就在本文简 ...

  6. python简单绘图步骤_python画图的两种方法

    python如何画图?这里给大家介绍两款python绘图的库:turtle和Matplotlib. 1 安装turtle Python2安装命令:pip install turtule Python3 ...

  7. python链表实现多项式_Python链表之两数之和

    两数之和 [今日知图] 标记 某一块代码可能需要稍后处理 使用m增加一个标记,标记名称可以是a~z和A~Z之间的任意一个字母; 添加标记了的行如果被删除,标记同时被删除; 后面的标记名与前面一致会覆盖 ...

  8. python timer 死掉_Python timer定时器两种常用方法解析 Python中如何在一段时间后停止程序...

    如何调用定时器 python 如何在python里面for循环中放了一个定时函数,当定# 我的构想程序效果 for Img in ImgArray: timer = threading.Timer(1 ...

  9. python 多列对齐_python – 如何连接两个数据帧并在这样的特定列上对齐?

    我想通过加入两个数据框并在特定列上对齐来做一些工作,如下所示: 数据框左边像: dict1={'abstract': {0: 'A1', 1: 'A2', 2: 'A3', 3: 'B1', 4: ' ...

最新文章

  1. Java堆内存分配与回收策略
  2. 独家 | 深度学习 V.S. 谜题游戏
  3. Delphi控制Excel输出上标示例
  4. 传递list对象作为参数_24.scala的隐式参数
  5. java字数统计_java统计字数
  6. 全面拥抱HDR时机已到?
  7. Entity Framework Core 2.1带来更好的SQL语句生成方案
  8. fopen_s不接受两个参数_如何利用SPC来管理制造过程,不懂SPC还敢混“质”场?
  9. php sql查询两个表语句,sql多表查询语句与方法
  10. android 5.1禁止休眠
  11. 计算机软件设计费计算标准,水利工程咨询、勘测设计费收费标准及计算程序
  12. 南理工计算机学院宋杰,周骏 - 计算机与信息科学学院 - Powered by 西南大学
  13. 计算机入门学习编程的建议
  14. python matplotlib 论文画图代码总结
  15. 【转载】androidstudo如何跨越这个厚厚的墙,亲测有效 Could not resolve com.android.tools.build:gradle:
  16. Linux基础,系统概叙与虚拟机搭建+CentOS系统安装(建议收藏)
  17. 10W+的爆款文章都是如何写出来的?
  18. 嵌入式行业的发展前景?
  19. The type java.lang.Object cannot be resolved It is indirectly referenced ... .
  20. php的laravel框架下载,PHP 之 Laravel 框架安装及相关开源软件

热门文章

  1. android编译集成dialer应用,Comet Android Dialer
  2. 职场选择,也许该考虑一下“增长黑客”?
  3. 项目管理 | 如何进行项目风险识别?
  4. Flash鼠标拖尾效果
  5. 我爬取了猪八戒网站的信息,发现了程序员做威客不得不进的坑
  6. Tomcat 打印日志让你事半功倍
  7. 12.4 C++常引用
  8. vue3实现搜索功能
  9. 文件上传漏洞及解析漏洞总结
  10. HAProxy配置详解