在支持后者的 Python 版本中,
list(iterable)
和 [*iterable]
之间有任何实际区别吗?
list(x)
是函数,[*x]
是表达式。您可以重新分配 list
,并使其执行其他操作(但您不应该这样做)。
谈论 cPython,
b = list(a)
翻译为以下字节码序列:
LOAD_NAME 1 (list)
LOAD_NAME 0 (a)
CALL_FUNCTION 1
STORE_NAME 2 (b)
相反,
c = [*a]
变为:
LOAD_NAME 0 (a)
BUILD_LIST_UNPACK 1
STORE_NAME 3 (c)
所以你可以说
[*a]
可能 稍微更有效,但也只是稍微有效一点。
dis
来研究函数生成的字节码。在这种情况下:
import dis
def call_list(x):
return list(x)
def unpacking(x):
return [*x]
dis.dis(call_list)
# 2 0 LOAD_GLOBAL 0 (list)
# 2 LOAD_FAST 0 (x)
# 4 CALL_FUNCTION 1
# 6 RETURN_VALUE
dis.dis(unpacking)
# 2 0 LOAD_FAST 0 (x)
# 2 BUILD_LIST_UNPACK 1
# 4 RETURN_VALUE
所以有一个区别,不仅仅是加载全局定义的名称
list
,这不需要在解包时发生。所以归根结底就是内置 list
函数是如何定义的以及 BUILD_LIST_UNPACK
到底是做什么的。
请注意,两者实际上比为此编写标准列表理解要少得多的代码:
def list_comp(x):
return [a for a in x]
dis.dis(list_comp)
# 2 0 LOAD_CONST 1 (<code object <listcomp> at 0x7f65356198a0, file "<ipython-input-46-dd71fb182ec7>", line 2>)
# 2 LOAD_CONST 2 ('list_comp.<locals>.<listcomp>')
# 4 MAKE_FUNCTION 0
# 6 LOAD_FAST 0 (x)
# 8 GET_ITER
# 10 CALL_FUNCTION 1
# 12 RETURN_VALUE
执行相同操作的两个构造之间总会存在一些差异。事实是,我不会说这种情况下的差异实际上是“实用的”。两者都是采用可迭代对象、迭代它然后从中创建列表的表达式。 契约是相同的:输入是可迭代的,输出是由可迭代元素填充的列表。
是的,
list
可以反弹到不同的名称;
list(it)
是函数调用,[*it]
是列表显示;对于较小的可迭代对象,[*it]
速度更快,但通常对于较大的可迭代对象执行相同的操作。哎呀,人们甚至可以提出这样一个事实:[*it]
减少了三个击键。这些实用吗?当我尝试从可迭代对象中获取列表时,我会想到它们吗?好吧,也许击键是为了保持在 79 个字符以下,并让 linter 将其关闭。
things = list(
get_things(
numerous and long arguments,
)
)
而 [*] 语法通过将代码缩短为来节省两行和缩进级别
[*things] = get_things(
numerous and long arguments,
)