Python: 今天你赋值了吗?

[EDIT] 没想到会被转发。很高兴也很囧。看来眼球多了的确容易纠错。要当心啊当心。编程

编程语言就是这样。有时候你已经用它写了数万行代码,自觉得很熟了。知道某一天遇到一个意料以外的问题。而后你才发现,原来TMD是这样的。app

问题

def change(l1, l2):
    l1.append(10)
    l2 = [7, 5, 3, 1]


list1 = [1, 3, 5, 7]
list2 = [3, 3, 3, 3]
change(list1, list2)
print(list1)
print(list2)

以上这段代码的输出是什么?若是你的答案是编程语言

[1, 3, 5, 7, 10]
[3, 3, 3, 3]

那么大牛请安静的离开。若是不是,能够读一下下面的故事。函数

赋值

最简单的赋值ui

list1 = [1, 3, 5, 7]

到底发生了什么呢?一个变量list1被赋值了?太笼统了吧。
对于Python来讲,Everything is an object。上面这行代码实际上是这样的:code

  1. 建立一个list对象对象

  2. 建立一个名字内存

  3. 绑定string

再来看个例子:hash

s1 = "hello"
s2 = "hello"
print(s1 is s2)

这个会输出什么?True! 由于是这样的:

  1. 建立一个string对象

  2. 建立名字s1并绑定到string对象

  3. 建立名字s2并绑定到string对象

s1s2绑定的是同一个对象!

涨姿式了。那么问题又来了:

l1 = [1, 3, 5, 7]
l2 = [1, 3, 5, 7]
print(l1 is l2)

结果是?。。。。。。必定是True了吧?错!

这里又有一个很重要的概念。Mutable 和 immutable。"hello"[1, 3, 5, 7]的区别就在于:前者是Immutable然后者是Mutable。对于可变的对象,Python会自动生成全新的对象,以确保各对象能够独立的变更。而不可变的就不须要 - 节省内存。

传参

回到开始的问题。当list1被传给change()的时候,究竟是什么被传过去了?让咱们来作一个小实验。在传入前和change()内部各加入一个语句

print(id(list1))

print(id(l1))

这里要解释一下,Python的每个对象都有一个惟一的hash id。id()这个函数就是用来打印出这个id的。加了上面两行以后,咱们会发现。在change()函数以外与以内,两个值是同样的。也就是说Python是把同一个对象传给了change()

这下咱们就能够理解为何list1在调用了change()以后被改动了。可是那么list2也应该被改动了才对啊?非也。这里,list2所绑定的对象的确被传进来了,的确生成了一个新list对象。可是这个对象被绑定给了l2。跟list2没半毛钱关系。咱们在作一个实验验证一下。

def change(l1, l2):
    l1.append(10)
    l2 = [7, 5, 3, 1]


list1 = [1, 3, 5, 7]
list2 = [3, 3, 3, 3]
change(list1, list2)
print(list1)
print(list2)

import dis
dis.dis(change)

对比开始的代码,咱们加入了最后两句。这个两句使用了dis模块把change反编译了。结果以下:

2 0 LOAD_FAST 0 (l1)

3 LOAD_ATTR                0 (append)
          6 LOAD_CONST               1 (10)
          9 CALL_FUNCTION            1
         12 POP_TOP

3 13 LOAD_CONST 2 (7)

16 LOAD_CONST               3 (5)
         19 LOAD_CONST               4 (3)
         22 LOAD_CONST               5 (1)
         25 BUILD_LIST               4
         28 STORE_FAST               1 (l2)
         31 LOAD_CONST               0 (None)
         34 RETURN_VALUE

对应原来的行号3,咱们能够看到,Python用4个常数build了一个list。而后把这个绑定到了名字l2而后就返回了。

至此真相大白。

相关文章
相关标签/搜索