如何使用方括号实现Python列表初始化?它是否会在创建列表之前逐个调用一些预先存在的函数,例如__setitem__
,或者是否有一个单独的函数,它接受可变数量的参数并创建一个列表?
l = [1, 2, 3, 4, 5]
例如,上面的列表是如何创建的?我感兴趣的是在源代码中构建列表的过程。实际代码本身,或执行将列表存储到内存中的步骤。
答案 0 :(得分:3)
您可以编译该特定代码段并使用dis
检查字节代码:
In [1]: import dis
In [2]: code = compile('l = [1, 2, 3, 4, 5]', '', 'exec')
In [3]: dis.dis(code)
1 0 LOAD_CONST 0 (1)
3 LOAD_CONST 1 (2)
6 LOAD_CONST 2 (3)
9 LOAD_CONST 3 (4)
12 LOAD_CONST 4 (5)
15 BUILD_LIST 5
18 STORE_NAME 0 (l)
21 LOAD_CONST 5 (None)
24 RETURN_VALUE
特别是第15行BUILD_LIST
,其中实际构建了列表。实际上没有调用构造函数或函数,如下例所示:
In [1108]: dis.dis(compile('l = list()', '', 'exec'))
1 0 LOAD_NAME 0 (list)
3 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
6 STORE_NAME 1 (l)
9 LOAD_CONST 0 (None)
12 RETURN_VALUE
在第3行CALL_FUNCTION
中看到的地方,调用list
类'构造函数。
语言的语法解析源代码以构建解析树,以便创建列表并准备在运行时使用。
有关列表对象实现的详细信息,请查看here。
编辑:找到字节码的实现细节。 Here它是。 BUILD_LIST
:
TARGET(BUILD_LIST)
x = PyList_New(oparg);
if (x != NULL) {
for (; --oparg >= 0;) {
w = POP();
PyList_SET_ITEM(x, oparg, w);
}
PUSH(x);
DISPATCH();
}
break;
与CALL_FUNCTION
对比:
TARGET(CALL_FUNCTION)
{
PyObject **sp;
PCALL(PCALL_ALL);
sp = stack_pointer;
#ifdef WITH_TSC
x = call_function(&sp, oparg, &intr0, &intr1);
#else
x = call_function(&sp, oparg);
#endif
stack_pointer = sp;
PUSH(x);
if (x != NULL)
DISPATCH();
break;
}
后者进行实际的函数调用,而前者使用PyList_New
分配对象。
答案 1 :(得分:3)
就语言规范而言,它构建了一个列表,并且没有关于它如何做到的承诺。如果您希望使用公共挂钩来自定义流程或其他内容,请不要。你不能这样做。
如果你想看看实现如何适用于你所使用的Python版本,你可以挖掘。例如,在CPython 3.6上,使用dis
,Python反汇编程序:
>>> import dis
>>> dis.dis(lambda: [1,2,3,4,5])
1 0 LOAD_CONST 1 (1)
2 LOAD_CONST 2 (2)
4 LOAD_CONST 3 (3)
6 LOAD_CONST 4 (4)
8 LOAD_CONST 5 (5)
10 BUILD_LIST 5
12 RETURN_VALUE
您可以看到带有参数BUILD_LIST
的{{1}}操作码。这会从Python字节码参数堆栈中弹出5个值,并从中构建一个列表。
查看5
的{{3}}:
BUILD_LIST
我们可以看到它使用TARGET(BUILD_LIST) {
PyObject *list = PyList_New(oparg);
if (list == NULL)
goto error;
while (--oparg >= 0) {
PyObject *item = POP();
PyList_SET_ITEM(list, oparg, item);
}
PUSH(list);
DISPATCH();
}
分配列表对象,并使用PyList_New
设置项目,这些例程是列表source的一部分。不涉及Python端API - 不查找PyList_SET_ITEM
名称,不list
或__setitem__
。