根据我的意识,使用[], {}
或()
实例化对象分别返回list, dict
或tuple
的新实例;带有新标识的新实例对象。
在我实际测试之前,这一点非常清楚,我注意到() 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)
。 如果没有给出参数,构造函数会创建一个新的空元组()
。
可以说,这还不足以回答我的问题。
那么,为什么空元组具有相同的身份,而其他像列表或词典则没有?
答案 0 :(得分:53)
Python在内部创建一个C
元组对象列表,其第一个元素包含空元组。每次使用tuple()
或()
时,Python都将返回上述C
列表中包含的现有对象,而不是创建新对象。
dict
或list
对象不存在这样的机制,相反,每次从头开始重新创建。
这很可能与以下事实有关:不可变对象(如元组)无法更改,因此保证在执行期间不会更改。考虑到frozenset() is frozenset()
返回True
时,这进一步巩固;比如()
一个空的frozenset
is considered an singleton in the implementation of CPython
。对于可变对象,这样的保证不存在,因此,没有动机缓存它们的零元素实例(即它们的内容可能随着身份保持不变而改变)。 / p>
请注意: 这不是一个人应该依赖的东西,即不应该将空元组视为单身。文档中没有明确提出这样的保证,因此应该假设它是依赖于实现的。
在最常见的情况下,CPython
的实现是使用两个宏PyTuple_MAXFREELIST
和PyTuple_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
使用不可变的缓存行为: