当'[]是[]'并且'{}是{}'返回False时,为什么'()是()'返回True?

时间:2016-07-12 12:25:48

标签: python python-3.x tuples identity python-internals

根据我的意识,使用[], {}()实例化对象分别返回list, dicttuple的新实例;带有新标识的新实例对象。

在我实际测试之前,这一点非常清楚,我注意到() 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)如果没有给出参数,构造函数会创建一个新的空元组()

可以说,这还不足以回答我的问题。

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

1 个答案:

答案 0 :(得分:53)

简而言之:

Python在内部创建一个C元组对象列表,其第一个元素包含空元组。每次使用tuple()()时,Python都将返回上述C列表中包含的现有对象,而不是创建新对象。

dictlist对象不存在这样的机制,相反,每次从头开始重新创建

这很可能与以下事实有关:不可变对象(如元组)无法更改,因此保证在执行期间不会更改。考虑到frozenset() is frozenset()返回True时,这进一步巩固;比如()一个空的frozenset is considered an singleton in the implementation of CPython。对于可变对象,这样的保证不存在,因此,没有动机缓存它们的零元素实例(即它们的内容可能随着身份保持不变而改变)。 / p>

请注意: 这不是一个人应该依赖的东西,即不应该将空元组视为单身。文档中没有明确提出这样的保证,因此应该假设它是依赖于实现的。

如何完成:

在最常见的情况下,CPython的实现是使用两个宏PyTuple_MAXFREELISTPyTuple_MAXSAVESIZE设置为正整数编译的。这些宏的正值会导致创建大小为PyTuple_MAXSAVESIZE的{​​{3}}。

使用参数PyTuple_New调用size == 0时,如果该列表尚未存在,请确保array of tuple objects到列表中:

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

然后,如果请求一个新的空元组,那么将返回位于add a new empty tuple的那个,而不是一个新的实例:

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

引发激励的另一个原因是函数调用构造一个元组来保存将要使用的位置参数。这可以在ceval.c中的first position of this list函数中看到:

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

在同一个文件中通过load_args调用。如果参数na为零,则返回空元组。

从本质上讲,这可能是一个经常执行的操作,因此每次都不重建一个空元组是有意义的。

进一步阅读:

更多回答阐明CPython使用不可变的缓存行为:

  • 对于整数,可以找到另一个挖掘源代码的答案do_call
  • 对于字符串,可以找到一些答案hereherehere