据我所知,使用 []、{}
或 ()
实例化对象会返回 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()
中找到的唯一相关信息状态:
[...] For example,
tuple('abc')
returns('a', 'b', 'c')
andtuple([1, 2, 3])
returns(1, 2, 3)
. If no argument is given, the constructor creates a new empty tuple,()
.
可以说,这不足以回答我的问题。
那么,为什么空元组具有相同的身份,而列表或字典等其他人则没有?
最佳答案
简而言之:
Python 在内部创建一个 C
元组对象列表,其第一个元素包含空元组。每次使用 tuple()
或 ()
时,Python 都会返回上述 C
列表中包含的现有对象,而不是创建新对象.
对于 dict
或 list
对象不存在这种机制,相反,每次都从头开始重新创建。
这很可能与不可变对象(immutable对象)(如元组)无法更改,因此保证在执行期间不会更改的事实有关。当考虑到 frozenset() is frozenset()
返回 True
; 时,这一点进一步巩固。比如 ()
一个空的 frozenset
is considered an singleton in the implementation of CPython
.对于可变对象,没有这样的保证,因此,没有动机缓存它们的零元素实例(即它们的内容可能会随着身份保持不变而改变)。
注意: 这不是人们应该依赖的东西,即不应将空元组视为单例。文档中没有明确做出这样的保证,所以应该假设它是依赖于实现的。
它是怎么做的:
在最常见的情况下,CPython
的实现是用两个宏编译的 PyTuple_MAXFREELIST
和 PyTuple_MAXSAVESIZE
设置为正整数。这些宏的正值导致创建 array of tuple
objects大小为 PyTuple_MAXSAVESIZE
.
当使用参数 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.. */
导致这样做的另一个原因是函数调用构造一个元组来保存将要使用的位置参数。这可以在 load_args
中看到ceval.c
中的函数:
static PyObject *
load_args(PyObject ***pp_stack, int na)
{
PyObject *args = PyTuple_New(na);
/* rest snipped for brevity.. */
通过 do_call
调用在同一个文件中。如果参数 na
的数量为零,则将返回一个空元组。
本质上,这可能是一个频繁执行的操作,因此不要每次都重建一个空元组是有意义的。
延伸阅读:
更多答案阐明了 CPython
的不可变缓存行为:
关于python - 为什么 '() is ()'和 '[] is []'返回False时 '{} is {}'返回True?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38328857/