据我所知,使用[], {}()实例化对象会分别返回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相关问答推荐

为什么我的Selenium脚本在密码元素上失败?

Strawberry FastAPI:如何调用正确的函数?

在python内的powershell中转义$_

Python 舍入数字不准确

Python中根据分组/ID对两个数据框进行映射,以更接近值的升序排列

Python (pandas) - 判断一个 df 中的值是否在另一个(不相等)df 中的任何对之间

在 groupby 之后,Pandas 在特定类别中获得最常见和最后的值

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

仅当从 USB 摄像头接收到新图像时才处理图像

当参数名称与 typing.Protocol 中定义的名称不同时发生 mypy 错误

有没有办法使用重采样矢量化添加缺失的月份?

通过最接近的匹配合并两个不同长度的列上的两个数据框

PyQt:退出时没有错误消息(回溯)

Python:pprint的模块错误,打印没有错误

numpy.ndarray 与 pandas.DataFrame

Python 3 变量名中接受哪些 Unicode 符号?

Python 3.10 模式匹配 (PEP 634) - 字符串中的通配符

如何使 Python3 成为 Geany 中的默认 Python

使用打印时,用+连接是否比用,分隔更有效?

是否可以在每个路由的基础上限制 Flask POST 数据大小?