代码改变世界

Python 笔试集(1):关于 Python 链式赋值的坑

2017-12-07 00:28  云物互联  阅读(625)  评论(0编辑  收藏  举报

前言

Python 的链式赋值是一种简易型批量赋值语句,一行代码即可为多个变量同时进行赋值。
例如:

x = y = z = 1

链式赋值是一种非常优雅的赋值方式,简单、高效且实用。但同时它也是一个危险的糖衣炸弹,值得我们引起关注。

面试题:求链式赋值语句中变量 x 的值?

x = [1, 2, 3, 4, 5]
i = 0
i = x[i] = 3
  • 如果你有过 C 语言的编程经验,那么你的思维习惯可能会让你得出这样的结果:x[0] 被赋值为 3,然后 i 再被赋值为 3,所以变量 x 的值为 [3, 2, 3, 4, 5]
  • 但实际上正确的答案却是:变量 i 首先被赋值为 3,然后 x[3] 再被赋值为3,所以最终变量 x 的值为 [1, 2, 3, 3, 5]

从上述结果的表现可以看出二者的区别为:C 语言的赋值顺序是 自右往左* 依次进行的,而 Python 则相反。*
造成这种表征区别的本质原因是由于 C 语言是一种「值语义」类型语言,而 Python 是「对象语义」类型语言。

值语义类型编程语言

值语义指使用系统标准的拷贝方式将一个源对象拷贝成为目标对象后,源对象与目标对象之间毫无关系,彼此独立存在,改变互不影响。
从这一特性出发, C 语言的赋值语句被设计成了具有返回值的表达式,如此才能够保证在链式赋值中所有的对象最终都是彼此独立的。
例如:

x = (y = 1)

表达式 y = 1 在将数值 1 赋值给对象 y 之后返回了数值 1 ,最后再将返回的数值 1 赋值给对象 x。虽然两次赋值操作的数值都是 1,但实际上 x 和 y 对象在内存中的地址是不相同的,所以 x 和 y 彼此独立。

对象语义类型编程语言

对象语义,也称为引用语义。指使用系统标准的拷贝方式将一个源对象拷贝成为目标对象后,源对象与目标对象之间依然共享底层资源,对任意一方的改变都将影响到另一方。
由于不需要像 C 语言那般要求对象之间的独立性。所以,Python 的赋值语句就仅仅是一条语句,而非表达式,也就不存在返回值。在 Python 的定义中,表达式是由操作符(Operator)连接而成的语句,但等号 = 并不属于 Python 的操作符,而是分隔符(Delimiters)。所以下列语句在 Python 中是非法的。

In : x = (y = 1)
  File "<ipython-input-6-f6b416f5f895>", line 1
    x = (y = 1)
           ^
SyntaxError: invalid syntax

因为 y = 1 并没有返回值,所以最后执行的是将 “没有返回值” 赋值给了变量 x,显然这样的语义是不存在意义的。

最后

从使用的角度来说,我们只需要简单的记住 Python 的链式赋值顺序是 自左往右 的即可。但更重要的是,通过该面试题我们了解到了 Python 实现的语义类型为对象语义,以及对象语义和值语义的区别。这非常有助于我们以后理解、解决或避免 Python 大多数隐含的坑。