返回介绍

8.7 Python 对不可变类型施加的把戏

发布于 2024-02-05 21:59:47 字数 1930 浏览 0 评论 0 收藏 0

 你可以放心跳过本节。这里讨论的是 Python 的实现细节,对 Python 用户来说没那么重要。这些细节是 CPython 核心开发者走的捷径和做的优化措施,对这门语言的用户而言无需了解,而且那些细节对其他 Python 实现可能没用,CPython 未来的版本可能也不会用。尽管如此,在学习别名和副本的过程中,你可能偶然见过这些把戏,因此我觉得有必要讲一下。

我惊讶地发现,对元组 t 来说,t[:] 不创建副本,而是返回同一个对象的引用。此外,tuple(t) 获得的也是同一个元组的引用。5 示例 8-20 证明了这一点。

5文档明确指出了这个行为。在 Python 控制台中输入 help(tuple),你会看到这句话:“如果参数是一个元组,那么返回值是同一个对象。”撰写这本书之前,我还以为自己对元组无所不知。

示例 8-20 使用另一个元组构建元组,得到的其实是同一个元组

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

❶ t1 和 t2 绑定到同一个对象。

❷ t3 也是。

str、bytes 和 frozenset 实例也有这种行为。注意,frozenset 实例不是序列,因此不能使用 fs[:](fs 是一个 frozenset 实例)。但是,fs.copy() 具有相同的效果:它会欺骗你,返回同一个对象的引用,而不是创建一个副本,如示例 8-21 所示。6

6copy 方法不会复制所有对象,这是一个善意的谎言,为的是接口的兼容性:这使得 frozenset 的兼容性比 set 强。两个不可变对象是同一个对象还是副本,反正对最终用户来说没有区别。

示例 8-21 字符串字面量可能会创建共享的对象

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

❶ 新建一个元组。

❷ t1 和 t3 相等,但不是同一个对象。

❸ 再新建一个字符串。

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

共享字符串字面量是一种优化措施,称为驻留(interning)。CPython 还会在小的整数上使用这个优化措施,防止重复创建“热门”数字,如 0、-1 和 42。注意,CPython 不会驻留所有字符串和整数,驻留的条件是实现细节,而且没有文档说明。

 千万不要依赖字符串或整数的驻留!比较字符串或整数是否相等时,应该使用 ==,而不是 is。驻留是 Python 解释器内部使用的一个特性。

本节讨论的把戏,包括 frozenset.copy() 的行为,是“善意的谎言”,能节省内存,提升解释器的速度。别担心,它们不会为你带来任何麻烦,因为只有不可变类型会受到影响。或许这些细枝末节的最佳用途是与其他 Python 程序员打赌,提高自己的胜算。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文