iterator.cycle 函数调用两次 return 同一个迭代器(具有相同的内存地址)

iterator.cycle function called twice return the same iterator (with same memory adress)

在python 3.7上,我希望每次调用iterator.cycle时都能得到一个新的内存地址(和循环),然而,这段代码在4:[=16上复制了2个迭代器=]

from itertools import cycle

cycles = [[None] * 2] * 2
s = set()
for i in range(2):
    for j in range(2):
        c =  cycle("ab")
        cycles[i][j] =c
        s.add(hex(id(c)))
        print(hex(id(c)))

print(len(s))

它打印:

0x7efe576cc370
0x7efe576cc3c0
0x7efe576cc410
0x7efe576cc370
3

所以对象0x7efe576cc370被使用了两次。另一方面,此代码有效:

from itertools import cycle

cycles = []

for _ in range(1000):
    c = cycle("ab")
    cycles.append(c)

s = set([hex(id(c)) for c in cycles])
print(len(s)) # 1000

每次迭代都会创建一个新对象,但是 id 函数的文档说明如下:

Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.

CPython implementation detail: This is the address of the object in memory.

您在第二个示例中证明了这一说法,您保留所有对象以便它们的生命周期重叠。

示例 1(x2 个唯一 ID):

>>> from itertools import cycle
>>> for i in range(5):
...     c = cycle('ab')
...     print(hex(id(c)))
... 
0x7fdac5d54140
0x7fdac5d54180
0x7fdac5d54140
0x7fdac5d54180
0x7fdac5d54140

示例 2(x5 个唯一 ID):

>>> store = []
>>> for i in range(5):
...     c = cycle('ab')
...     store.append(c)
...     print(hex(id(c)))
... 
0x7fdac5e20680
0x7fdac5d17340
0x7fdac5d17300
0x7fdac5d172c0
0x7fdac5d17240

这里的问题是您构建 cycles 对象的方式。

当您在列表上使用 * 运算符时,返回的新列表包含对原始列表的多个引用,而不是原始列表的副本。

因此,cycles 是一个包含两项的列表,其中两个元素指向同一个列表,因此对 cycles[1][0] 所做的更改也适用于 cycles[0][0]

当您分配给 cycles[1][0] 时,您还覆盖了 cycles[0][0] 处的项目,这会删除之前的项目。

至于为什么这意味着 s 的长度为 3,这是由于对象分配器/垃圾收集器交互的方式。

除了最里面的列表,您应该使用列表理解来构造对象。

 cycles = [[None] * 2 for _ in range(2)] 

这将生成一个适当构建的列表,您的代码将正常工作