复制列表时“[:]”和“[::]”切片之间的区别?

Difference between '[:]' and '[::]' slicing when copying a list?

我们可以使用 [:]:

(浅)复制一个 list
l = [1, 2, 3]
z1 = l[:]

我们也可以使用[::]:

(浅)复制它
z2 = l[::]

现在 z1 == z2 将是 True。阅读 Explain Python's slice notation.

中的答案后,我了解了这些切片的工作原理

但是,我的问题是,这两者在内部有什么区别吗?一个比另一个更有效还是他们做的事情完全一样?

绝对没有区别,至少在Python3。如果您愿意,可以使用 dis.dis 检查为每个生成的 byte-code:

l = [1, 2, 3, 4]

Byte-code 发出 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

同时,byte-code 为 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

如您所见,它们完全相同。两者都为用于构建切片 (BUILD_SLICE) 的 startstop 的值加载一些 None(两个 LOAD_CONSTS)并应用它。 Nones 是这些的默认值,如 Standard Type hierarchyslices 的文档所述:

Special read-only attributes: start is the lower bound; stop is the upper bound; step is the step value; each is None if omitted. These attributes can have any type.

[:],少key-strokes.


实际上有趣的是,在 Python 2.x 中生成的字节码是不同的,并且由于 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[:] 可能会稍微好一些。


当然不存在这种相似性仅适用于列表;它适用于 Python:

中的所有序列
# 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 language reference section 6.3.2, Subscriptions,序列的内部表达式的计算结果必须为整数或切片。这两个示例都产生相同的切片,因此是相同的。通过显式声明默认值(start=0stop=len(sequence) 或更多、step=1),还有许多其他切片具有相同的效果。