我正在研究functools.ru_cache的实现,突然发现了这个片段:
root = [] # root of the circular doubly linked list
root[:] = [root, root, None, None] # initialize by pointing to self
我熟悉循环列表和双链接列表。我还知道new_list = my_list[:]
创建了my_list的副本。在寻找切片分配或循环双链表的其他实现时,我找不到任何关于这种特定语法的进一步信息。
问题:
- 在这种情况下发生了什么
- 是否有不同的语法来实现相同的结果
some_list[:] = some_iterable
(没有自引用(是否有不同的常见用例
在中
root[:] = [root, root, None, None]
左边的切片分配只是说root
的引用被重用来保存右边部分的内容。
所以root
引用永远不会改变,是的,在列表中,你可以引用自己(但不要尝试对它们进行递归平坦化:(。在这种情况下,表示会显示"列表上的递归"。
>>> root
[<Recursion on list with id=48987464>,
<Recursion on list with id=48987464>,
None,
None]
打印时显示省略号:
>>> print(root)
[[...], [...], None, None]
请注意,您不需要为此分配切片。有一些简单的方法可以触发递归:
>>> root = []
>>> root.append(root)
>>> root
[<Recursion on list with id=51459656>]
>>>
众所周知,使用append
不会更改引用,它只是对列表进行变异,为自己添加一个引用。也许更容易理解。
- 在这种情况下发生了什么
如果l
是列表,则l[:] = items
调用l.__setitem__(slice(None), items)
。此方法在清除给定可迭代项之后,将其各自分配给列表。
- 是否有不同的语法来实现相同的结果
你可以做
l.clear()
l.extend(items)
some_list[:] = some_iterable (without the self reference)?
是否有不同的常见用例
理论上,您可以将任何可迭代项放入列表中。
只需查看已分解的代码:
In [1]: def initializer():
...: root = [] # root of the circular doubly linked list
...: root[:] = [root, root, None, None]
...:
In [2]:
In [2]: import dis
In [3]: dis.dis(initializer)
2 0 BUILD_LIST 0
2 STORE_FAST 0 (root)
3 4 LOAD_FAST 0 (root)
6 LOAD_FAST 0 (root)
8 LOAD_CONST 0 (None)
10 LOAD_CONST 0 (None)
12 BUILD_LIST 4
14 LOAD_FAST 0 (root)
16 LOAD_CONST 0 (None)
18 LOAD_CONST 0 (None)
20 BUILD_SLICE 2
22 STORE_SUBSCR
24 LOAD_CONST 0 (None)
26 RETURN_VALUE
您想要的是STORE_SUBSCR
操作代码,它可以实现以下功能:
mplements TOS1[TOS] = TOS2
这是由于做文件一个到位的操作。如果你想知道什么是就地操作,下面是文档如何定义的:
就地操作类似于二进制操作,它们删除TOS和TOS1,并将结果推回到堆栈上,但当TOS1支持该操作时,该操作会就地完成,生成的TOS可能是(但不一定是(原始TOS1。
这将验证源代码中的内联文档所说的内容:
通过指向self进行初始化。
关于您的其他问题:
是否有不同的语法来实现相同的结果?
是的,正如其他答案中提到的那样,您可以使用list.extend
属性清除并设置列表项。或者一个接一个地分配项目,也许lol
some_list[:]是否有不同的常见用例=some_iterable(没有自引用(?
这是一个非常模糊的问题,因为它是什么。以注入的方式分配项目,这可能有替换项目而不重新创建引用等好处。