据我所知,使用[], {}()实例化对象会分别返回list, dicttuple的新实例;一个带有a new identity的新实例对象.

这对我来说非常清楚,直到我实际测试它,我注意到() is ()实际上返回True,而不是预期的False:

>>> () is (), [] is [], {} is {}
(True, False, False)

正如预期的那样,在创建分别为list()dict()tuple()的对象时,这种行为也会表现出来:

>>> tuple() is tuple(), list() is list(), dict() is dict()
(True, False, False)

我能在the docs for tuple()个州找到的唯一相关信息:

[...] 例如,tuple('abc')返回('a', 'b', 'c')tuple([1, 2, 3])返回(1, 2, 3).If no argument is given, the constructor creates a new empty tuple, 104.

我只想说,这还不足以回答我的问题.

那么,为什么空元组具有相同的身份,而列表或字典等其他元组则没有?

推荐答案

简而言之:

Python在内部创建一个C个元组对象列表,其第一个元素包含空元组.每次使用tuple()()时,Python都会返回前面提到的C列表中包含的现有对象,而不会创建新对象.

这种机制不适用于dictlist个物体,相反,它们是recreated from scratch every time个.

这很可能与不可变对象(如元组)无法更改,并且保证在执行期间不会更改有关.考虑到frozenset() is frozenset()返回True,这一点进一步巩固;比如()个空的frozensetis considered an singleton in the implementation of CPython.对于可变对象,such guarantees are not in place和,因此,没有动机缓存它们的零元素实例(即它们的内容可能会随着标识保持不变而改变).

Take note: This isn't something one should depend on, i.e one shouldn't consider empty tuples to be singletons. No such guarantees are explicitly made in the documentation so one should assume it is implementation dependent.


如何做到:

在最常见的情况下,CPython的实现是通过将两个宏PyTuple_MAXFREELISTPyTuple_MAXSAVESIZE设置为正整数来编译的.这些宏的正值导致创建大小为PyTuple_MAXSAVESIZEarray of tuple objects.

当用参数size == 0调用PyTuple_New时,如果它不存在,则确保列表中的add a new empty tuple:

if (size == 0) {
    free_list[0] = op;
    ++numfree[0];
    Py_INCREF(op);          /* extra INCREF so that this is never freed */
}

然后,如果请求一个新的空元组,将返回位于first position of this list中的元组,而不是一个新实例:

if (size == 0 && free_list[0]) {
    op = free_list[0];
    Py_INCREF(op);
    /* rest snipped for brevity.. */

促使这样做的另一个原因是函数调用构造一个元组来保存将要使用的位置参数.这可以在ceval.c中的load_args函数中看到:

static PyObject *
load_args(PyObject ***pp_stack, int na)
{
    PyObject *args = PyTuple_New(na);
    /* rest snipped for brevity.. */

在同一个文件中通过do_call调用.如果参数na的数量为零,将返回一个空元组.

本质上,这可能是一个频繁执行的操作,因此不必每次都重建空元组是有意义的.


进一步阅读:

另外两个答案揭示了CPython在不可变条件下的缓存行为:

  • 对于整数,可以在源代码中找到另一个答案here.
  • 对于字符串,可以在hereherehere中找到少量答案.

Python-3.x相关问答推荐

根据收件箱内部的值以行降序的特定顺序重新排序列

数组列的极点成对求和

While循环不停止地等待,直到时间.睡眠结束

PySpark每毫秒使用先前的值填充数据

为什么 tkinter 在 tkinter 窗口外计算鼠标事件?

将自定义函数应用于 pandas 数据框的每一列

在Pandas中,根据另一列中的重复值将数据分组为一列

「Python Pandas」多级索引列和行匹配,如果列和行名称相似,则排除这些单元格中的值添加

使用 NaN 计算 pct_change 时如何避免 bfill 或 ffill

如何将搜索结果中的所有值保存在另一个列表中?

Keras 中 Conv2D 层的意外结果

裁剪复数以解决 exp 中的溢出错误

如何通过 GitLab V4 api 列出 gitlab 项目中的所有项目变量

Python ** 用于负数

使用 pandas 进行多类分类的总体准确度

UnicodeDecodeError:utf-8编解码器无法解码位置 1 的字节 0x8b:无效的起始字节,同时读取Pandas中的 csv 文件

如何模拟 Django 模型对象(及其方法)?

如何在多核上运行 Keras?

在 ubuntu 20.04 中安装 libpq-dev 时出现问题

如何将文档字符串放在 Enums 上?