python引用的不可变性以及tuple真的不可变吗

== 运算符比较两个对象的值(对象中保存的数据) , 而 is 比较对象的 标识。 通常, 我们关注的是值, 而不是标识, 因此 Python 代码中 == 出现的频 率比 is 高。 然而, 在变量和单例值之间比较时, 应该使用 is。 目前, 最常使用 is 检查变量绑定的值是不是 None。 下面是推荐的写法: x is None 否定的正确写法是: x is not None is 运算符比 == 速度快, 因为它不能重载, 所以 Python 不用寻找并调用 特殊方法, 而是直接比较两个整数 ID。 而 a == b 是语法糖, 等同于 a.eq(b)。 继承自 object 的 eq 方法比较两个对象的 ID, 结 果与 is 一样。 但是多数内置类型使用更有意义的方式覆盖了 eq 方法, 会考虑对象属性的值。 相等性测试可能涉及大量处理工作, 例 如, 比较大型集合或嵌套层级深的结构时。 在结束对标识和相等性的讨论之前, 我们来看看著名的不可变类型 tuple(元组) , 它没有你想象的那么一成不变。

元组与多数 Python 集合(列表、 字典、 集, 等等) 一样, 保存的是对象 的引用。 如果引用的元素是可变的, 即便元组本身不可变, 元素依然 可变。 也就是说, 元组的不可变性其实是指 tuple 数据结构的物理内容(即保存的引用) 不可变, 与引用的对象无关。 而 str、 bytes 和 array.array 等单一类型序列是扁平的, 它们保存的不是引用, 而是在连 续的内存中保存数据本身(字符、 字节和数字) 。 示例 8-5 表明, 元组的值会随着引用的可变对象的变化而变。 元组中不 可变的是元素的标识

>>> t1 = (1, 2, [30, 40]) 
>>> t2 = (1, 2, [30, 40])
>>> t1 == t2
True
>>> id(t1[-1])
4302515784
>>> t1[-1].append(99)
>>> t1
(1, 2, [30, 40, 99])
>>> id(t1[-1])
4302515784
>>> t1 == t2
False

复制:

默认做浅复制,copy.deepcopy深复制

传参:

这种方案的结果是, 函数可能会修改作为参数传入的可变对象, 但是无 法修改那些对象的标识(即不能把一个对象替换成另一个对象) 。 示例 8-11 中有个简单的函数, 它在参数上调用 += 运算符。 分别把数字、 列表和元组传给那个函数, 实际传入的实参会以不同的方式受到影响

可变的受影响,不可变没影响

不要使用可变类型作为参数的默认值

Python对不可变类型施加的把戏 捷径和做的优化措施, 对这门语言的用户而言无需了解, 而且那些细节对其他 Python 实现可能没用, CPython 未来的版本可能也不会用。 尽管如此, 在学习别名和副本的过程中, 你可能偶然见过这些 把戏, 因此我觉得有必要讲一下。

我惊讶地发现, 对元组 t 来说, t[:] 不创建副本, 而是返回同一个对 象的引用。 此外, tuple(t) 获得的也是同一个元组的引用。 示例 8- 20 证明了这一点。 文档明确指出了这个行为。 在 Python 控制台中输入 help(tuple), 你会看到这句话: “如果 参数是一个元组, 那么返回值是同一个对象。 ”撰写这本书之前, 我还以为自己对元组无所不 知。

>>> t1 = (1, 2, 3)
>>> t2 = tuple(t1)
>>> t2 is t1
True
>>> t3 = t1[:]
>>> t3 is t1
True

再来看看短字符串

>>> t1 = (1, 2, 3)
>>> t3 = (1, 2, 3) # ➊
>>> t3 is t1 # ➋
False
>>> s1 = 'ABC'
>>> s2 = 'ABC' # ➌
>>> s2 is s1 # ➍
True

奇怪的事发生了, a 和 b 指代同一个字符串

共享字符串字面量是一种优化措施, 称为驻留(interning) 。 CPython 还 会在小的整数上使用这个优化措施, 防止重复创建“热门”数字, 如 0、 -1 和 42。 注意, CPython 不会驻留所有字符串和整数, 驻留的条件 是实现细节, 而且没有文档说明。 千万不要依赖字符串或整数的驻留! 比较字符串或整数是否 相等时, 应该使用 ==, 而不是 is。 驻留是 Python 解释器内部使用 的一个特性

简单的赋值不创建副本。 对 += 或 *= 所做的增量赋值来说, 如果左边的变量绑定的是不可变 对象, 会创建新对象; 如果是可变对象, 会就地修改。 为现有的变量赋予新值, 不会修改之前绑定的变量。 这叫重新绑 定: 现在变量绑定了其他对象。 如果变量是之前那个对象的最后一 个引用, 对象会被当作垃圾回收。 函数的参数以别名的形式传递, 这意味着, 函数可能会修改通过参 数传入的可变对象。 这一行为无法避免, 除非在本地创建副本, 或 者使用不可变对象(例如, 传入元组, 而不传入列表) 。 使用可变类型作为函数参数的默认值有危险, 因为如果就地修改了 参数, 默认值也就变了, 这会影响以后使用默认值的调用。

原文地址:https://www.cnblogs.com/handso/p/14074022.html