我们可以(浅)使用list
复制[:]
:
l = [1, 2, 3]
z1 = l[:]
我们也可以(浅)使用[::]
复制它:
z2 = l[::]
现在z1 == z2
将是True
。在Explain Python's slice notation阅读答案后,我了解这些切片的工作原理。
但是,我的问题是,这两者内部有什么区别吗?复制中一个比另一个更有效吗,还是做同样的事情?
答案 0 :(得分:19)
它们之间完全没有区别,至少在Python 3中。如果您愿意,可以使用dis.dis
检查为每个字节代码生成的字节代码:
l = [1, 2, 3, 4]
为l[:]
发出的字节码:
from dis import dis
dis('l[:]')
1 0 LOAD_NAME 0 (l)
3 LOAD_CONST 0 (None)
6 LOAD_CONST 0 (None)
9 BUILD_SLICE 2
12 BINARY_SUBSCR
13 RETURN_VALUE
while,为l[::]
发出字节码:
dis('l[::]')
1 0 LOAD_NAME 0 (l)
3 LOAD_CONST 0 (None)
6 LOAD_CONST 0 (None)
9 BUILD_SLICE 2
12 BINARY_SUBSCR
13 RETURN_VALUE
如您所见,它们完全相同。两者都为None
和LOAD_CONSTS
的值加载一些start
(两个stop
),用于构建切片(BUILD_SLICE
)并应用它。如Standard Type hierarchy中None
的文档中所述slices
是start
的默认设置:
特殊只读属性:
lower
是stop
界限;step
是上限;step
是None
值;如果省略,则每个[:]
。这些属性可以是任何类型。
使用2.x
,它的击键次数更少。
实际上有趣的是,在Python l[:]
中生成的字节代码是不同的,由于>>> def foo():
... l[:]
...
>>> dis(foo)
2 0 LOAD_GLOBAL 0 (l)
3 SLICE+0
4 POP_TOP
5 LOAD_CONST 0 (None)
8 RETURN_VALUE
的命令较少,它可能稍微更高性能:
l[::]
同时,对于>>> def foo2():
... l[::]
...
>>> dis(foo2)
2 0 LOAD_GLOBAL 0 (l)
3 LOAD_CONST 0 (None)
6 LOAD_CONST 0 (None)
9 LOAD_CONST 0 (None)
12 BUILD_SLICE 3
15 BINARY_SUBSCR
16 POP_TOP
17 LOAD_CONST 0 (None)
20 RETURN_VALUE
:
l[:]
即使我没有计时(但我不会,差异应该很小),似乎由于需要的指令较少,# Note: the Bytecode class exists in Py > 3.4
>>> from dis import Bytecode
>>>
>>> Bytecode('(1, 2, 3)[:]').dis() == Bytecode('(1, 2, 3)[::]').dis()
True
>>> Bytecode('"string"[:]').dis() == Bytecode('"string"[::]').dis()
True
可能稍好一些。
这种相似性当然不存在仅适用于列表;它适用于Python中的所有序列:
{{1}}
与其他人类似。
答案 1 :(得分:3)
对于Python language reference section 6.3.2, Subscriptions,序列的内部表达式必须求值为整数或切片。这两个例子都产生相同的切片,因此是相同的。通过明确声明默认值(utl_file
,start=0
或更多,stop=len(sequence)
),还有许多其他切片具有相同的效果。