复制列表时“[:]”和“[::]”切片之间的区别?
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
) 的 start
和 stop
的值加载一些 None
(两个 LOAD_CONSTS
)并应用它。 None
s 是这些的默认值,如 Standard Type hierarchy 中 slices
的文档所述:
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=0
、stop=len(sequence)
或更多、step=1
),还有许多其他切片具有相同的效果。
我们可以使用 [:]
:
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
) 的 start
和 stop
的值加载一些 None
(两个 LOAD_CONSTS
)并应用它。 None
s 是这些的默认值,如 Standard Type hierarchy 中 slices
的文档所述:
Special read-only attributes:
start
is thelower
bound;stop
is the upper bound;step
is thestep
value; each isNone
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=0
、stop=len(sequence)
或更多、step=1
),还有许多其他切片具有相同的效果。