前阵子我们聊了下函数的参数传递以及变量赋值的一些内容:关于函数参数传递,80%人都错了。 简单回顾下要点: 1. Python 中的变量不是装有对象的“容器”,而是贴在对象上的“标签”。 2. 参数传递相当于一次赋值:多贴了一个标签。 3. 至于在函数内部对参数的修改是否会影响到外部变量的值,取决于你怎样修改:如果是重新赋值就不会,如果是修改对象自身内容则会。 讲到这里就有个常被提及的概念: 在 Python 中, 不可变对象不允许对自身内容进行修改。如果我们对一个不可变对象进行赋值,实际上是生成一个新对象,再让变量指向这个对象。哪怕这个对象简单到只是数字 0 和 1: a = 0 输出: a 4463151440
a 4463151472 因为对象不可变,所以为了提高效率,Python 会使用一些公用的对象: a = 1 输出: a 4423761776
b 4423761776
True
True
c 4430180912
d 4430180912
True
True 这里顺便提一下 a = 2 输出: True
False 而可变对象则可以对自身内容进行修改,如: m = [1, 2, 3]
print('m', m, id(m))
m[1] = 4 输出: m [1, 2, 3] 4536815752
m [1, 4, 3] 4536815752
m [1, 4, 3, 5] 4536815752 可以看到,虽然 m 的值发生了变化,但是地址没变,还是原来那个 m。 上次我也说到,很多的教程都在用可变和不可变来谈论赋值和参数传递,我觉得这很不好。因为他们说到不可变对象时用的是赋值,而说到可变对象又用了 list 的索引、apeend 等方法,这根本是两码事。如果大家都是赋值,那么无论是否可变,效果都是一样的: m = [1, 2, 3]
print('m', m, id(m))
m = [4, 5, 6]
print('m', m, id(m)) 输出 m [1, 2, 3] 4329894024
m [4, 5, 6] 4329910856 所以理解了 Python 的赋值原理,就明白这与是否可变无关。而可变对象于不可变对象本身的不同仅在于一个可以修改变量的值,而另一个不允许。 基于这一设定,两者在功能上的最大区别就是:不可变对象可以作为字典 dict 的键 key,而可变对象不行。比如 list 不能作为字典的键,但 tuple 可以。 另外,明白了可变与不可变的区别,一些方法的效果也就自然理解了: s = 'abc' 输出: s abc
s2 adc
m [3, 2, 1]
m2 None 因为 不过,有个特殊情况需要注意: m = [1, 2, 3]
print('m', m, id(m))
m += [4]
print('m', m, id(m))
m = m + [5]
print('m', m, id(m)) 输出 m [1, 2, 3] 4494164104
m [1, 2, 3, 4] 4494164104
m [1, 2, 3, 4, 5] 4494181128
如果我们就是需要产生一个 list 对象的副本,可以通过 m = [1, 2, 3]
print('m', m, id(m))
n = m[:]
print('n', n, id(n))
n[1] = 4 这样对 n 的修改便不再会影响到 m,因为它们已不是同一个对象。 那么如果是这样呢: m = [1, 2, [3]]
n = m[:]
n[1] = 4 猜一猜 m 的结果是什么? 再去 Python 里执行下看看输出,是不是和预期一样,想想为什么?这个牵涉到浅拷贝、深拷贝的概念,我们下次再聊。 |
|