简而言之:
Python在内部创建一个C
个元组对象列表,其第一个元素包含空元组.每次使用tuple()
或()
时,Python都会返回前面提到的C
列表中包含的现有对象,而不会创建新对象.
这种机制不适用于dict
或list
个物体,相反,它们是recreated from scratch every time个.
这很可能与不可变对象(如元组)无法更改,并且保证在执行期间不会更改有关.考虑到frozenset() is frozenset()
返回True
,这一点进一步巩固;比如()
个空的frozenset
个is 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_MAXFREELIST
和PyTuple_MAXSAVESIZE
设置为正整数来编译的.这些宏的正值导致创建大小为PyTuple_MAXSAVESIZE
的array 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
在不可变条件下的缓存行为: