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

正则表达式:匹配字符串中的分隔符(字母和特殊字符)以形成新的子字符串

Pandas 值列中列表中元素的计数

魔术8球txt文件列表

spinbutton调整up/down箭头

将名字转换成姓氏、首字母和中间字母的格式

通过 requests 库调用 API 获取访问令牌

根据条件过滤元组列表

未在 Django 中定义主键类型警告时使用的自动创建主键

在带有 M1 芯片(基于 ARM 的 Apple Silicon)的 Mac 上安装较早版本的 Python(3.8 之前)失败

ValueError:FixedLocator 位置的数量 (5),通常来自对 set_ticks 的调用,与刻度标签的数量 (12) 不匹配

尾部斜杠的 FastAPI 重定向返回非 ssl 链接

如何在不更改任何默认值的情况下在 Ubuntu 上为 python 3.8 安装 pip?

如果我将默认设置为无,我可以省略 Optional 吗?

判断 dict.items() 中的成员资格的时间复杂度是多少?

在 Ipython 中使用 Pylint (Jupyter-Notebook)

Python 3.8 的pip

Python中的依赖倒置

如何在不使用 @hydra.main() 的情况下获取 Hydra 配置

asyncio 中所有这些已弃用的“循环”参数是什么?

try 注释散列变量时,“ABCMeta”对象不可下标