摘要:不可变对象不允许对自身内容进行修改。因为他们说到不可变对象时用的是赋值,而说到可变对象又用了的索引等方法,这根本是两码事。基于这一设定,两者在功能上的最大区别就是不可变对象可以作为字典的键,而可变对象不行。
前阵子我们聊了下函数的参数传递以及变量赋值的一些内容:关于函数参数传递,80%人都错了
简单回顾下要点:
1. Python 中的变量不是装有对象的“ 容器 ”,而是贴在对象上的“ 标签 ”。
2. 参数传递相当于一次 赋值 :多贴了一个标签。
3. 至于在函数内部对参数的修改是否会影响到外部变量的值,取决于你怎样修改:如果是重新赋值就不会,如果是修改对象自身内容则会。
讲到这里就有个常被提及的概念:
可变对象和不可变对象
在 Python 中,
可变对象 包括 list、dict、set、自定义类型 等;
不可变对象 包括 int、float、bool、str、tuple 等。
不可变对象不允许对自身内容进行修改。如果我们对一个不可变对象进行赋值,实际上是生成一个新对象,再让变量指向这个对象。哪怕这个对象简单到只是数字 0 和 1:
a = 0 print("a", id(a)) a = 1 print("a", id(a))
输出:
a 4463151440 a 4463151472
因为对象不可变,所以为了提高效率,Python 会使用一些公用的对象:
a = 1 print("a", id(a)) b = 1 print("b", id(b)) print(a == b) print(a is b) c = "hello world" print("c", id(c)) d = "hello world" print("d", id(d)) print(c == d) print(c is d)
输出:
a 4423761776 b 4423761776 True True c 4430180912 d 4430180912 True True
这里顺便提一下 is 这个操作符。它和 == 的区别在于:== 只判断“值”是不是相等,而 is 则判断是否为同一个对象,也就是地址一致。比如:
a = 2 b = 2.0 print(a == b) print(a is b)
输出:
True False
而可变对象则可以对自身内容进行修改,如:
m = [1, 2, 3] print("m", m, id(m)) m[1] = 4 print("m", m, id(m)) m.append(5) print("m", m, id(m))
输出:
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" s2 = s.replace("b", "d") print("s", s) print("s2", s2) m = [1, 2, 3] m2 = m.reverse() print("m", m) print("m2", m2)
输出:
s abc s2 adc m [3, 2, 1] m2 None
因为 str 是不可变对象,所以它的方法如 replace、strip、upper 都不可能修改原对象, 只会返回一个新对象 ,比如重新赋值才可以。而 list 是可变对象,它的方法如 reverse、sort、append,都是 在原有对象上直接修改 ,无返回值。
不过,有个特殊情况需要注意:
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
m = m + 和 m += 虽然是一样的结果,但 m 指向的对象却发生了变化。原因在于,前者是做了赋值操作,而后者其实是调用的 __iadd__ 方法。
如果我们就是需要产生一个 list 对象的副本,可以通过 [:]:
m = [1, 2, 3] print("m", m, id(m)) n = m[:] print("n", n, id(n)) n[1] = 4 print("m", m) print("n", n)
这样对 n 的修改便不再会影响到 m,因为它们已不是同一个对象。
那么如果是这样呢:
m = [1, 2, [3]] n = m[:] n[1] = 4 n[2][0] = 5 print(m)
猜一猜 m 的结果是什么?
[1, 2, [3]]
[1, 4, [3]]
[1, 2, [5]]
[1, 4, [5]]
其它结果
再去 Python 里执行下看看输出,是不是和预期一样,想想为什么?这个牵涉到浅拷贝、深拷贝的概念,我们下次再聊。
════
其他文章及回答:
如何自学Python | 新手引导 | 精选Python问答 | Python单词表 | 区块链 | 人工智能 | 双11 | 嘻哈 | 爬虫 | 排序算法 | 我用Python | 高考 | 世界杯 | 竞猜 | requests
欢迎搜索及关注: Crossin的编程教室
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/42499.html
摘要:中的可变与不可变对象中的所有东西都是一个对象。和内置函数以整数形式返回对象的标识。更改可变对象很便宜。不可变的例外并非所有的不可变对象都是不可变的。所以引用该对象的变量不会改变,但对象本身正在改变,但仅在函数范围内。 Python中的可变与不可变对象 Python中的所有东西都是一个对象。每个Python新手都应该学习的是,Python中的所有对象都可以是可变的或不可变的。showIm...
摘要:变量在我们的编程中是最基础的概念,它就相当于我们盖大楼用的砖块一样不可或缺。理解变量的运行方式至关重要。虽然他们的食物是一样的。但是,像这样这样并没有新建对象,而是将的引用传递给了,他们都指向一个对象。这个就是的垃圾回收机制。 变量在我们的编程中是最基础的概念,它就相当于我们盖大楼用的砖块一样不可或缺。理解变量...
不可变对象 如果一个对象的状态在构造后不能改变,则该对象被认为是不可变的,对不可变对象的最大依赖被广泛认为是一种创建简单、可靠代码的合理策略。 不可变对象在并发应用程序中特别有用,由于它们不能改变状态,因此它们不会被线程干扰破坏或在不一致的状态下观察。 程序员通常不愿意使用不可变对象,因为他们担心创建新对象的成本而不是就地更新对象的成本,对象创建的影响经常被高估,并且可以通过与不可变对象相关联的一...
摘要:不要疑惑,告诉你答案这个代表正负号的正。虽然一点技术含量没有,但是你要懂序列也许叫可迭代对象更为合适,但是我喜欢叫序列。 数据结构 可变类型与不可变类型(重头戏) 基操: 可变类型:[], {} # 可增删改 查 不可变类型: int float str () # 无法增删改, 只可查 升操: + 与...
阅读 1156·2021-09-22 15:24
阅读 2254·2019-08-30 15:44
阅读 2599·2019-08-30 10:55
阅读 3334·2019-08-29 13:25
阅读 1618·2019-08-29 13:09
阅读 1364·2019-08-26 14:05
阅读 1342·2019-08-26 13:58
阅读 1970·2019-08-26 11:57