[英]list() vs iterable unpacking in Python 3.5+
在支持后者的 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 关闭它的击键。
显然,在 CPython 中存在性能差异,其中[*a]过度分配而list()不会: 是什么导致 [*a] 过度分配?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.