python 允许迭代突变的生成器函数
python generator function that is allowed to iterate over mutations
我想定义一个生成器函数,它可以遍历正在发生变化的字典对象。
def generator(d):
for k,v in sorted(d.items()):
yield (k,v)
d = {1:'a', 2:'x', 4:'m', 8:'d', 16:'f'}
i = generator(d)
print(next(i))
print(next(i))
del d[8]
print(next(i))
d[16] = "f"
d[32] = "z"
print(next(i))
print(next(i))
如果我执行上面的代码,我会得到:
(1, 'a')
(2, 'x')
(4, 'm')
(8, 'd')
(16, 'f')
所需的输出应该是:
(1, 'a')
(2, 'x')
(4, 'm')
(16, 'f')
(32, 'z')
我不明白的部分是当我将 d = {1:'a', 2:'x', 4:'m', 8:'d', 16:'f'}
分配给 i = generator(d)
时,字典已经添加到函数 generator(d)
中,并成为带有初始字典的生成器实例。因此,每当我使用 del
函数时,生成器都会以 d = {1:'a', 2:'x', 4:'m', 8:'d', 16:'f'}
作为参数保持不变。我将如何实现这一目标?
您需要按索引而不是按元素生成。您需要在每次 yield 之间重新生成列表以捕获更改。
def generator(d):
for i in range(len(d)):
dd = sorted(d.items())
if i < len(dd):
yield dd[i]
d = {1:'a', 2:'x', 4:'m', 8:'d', 16:'f'}
i = generator(d)
print(next(i))
print(next(i))
del d[8]
print(next(i))
d[16] = "f"
d[32] = "z"
print(next(i))
print(next(i))
输出
(1, 'a')
(2, 'x')
(4, 'm')
(16, 'f')
(32, 'z')
您需要处理异常以检查字典何时更改,并需要一个缓冲区来保存已生成的键。
def generator(d):
buffer = set()
while True:
old_hash = hash(frozenset(d.items()))
try:
for k, v in d.items():
if hash(frozenset(d.items())) != old_hash:
raise RuntimeError('dictionary changed size during iteration')
if (k, v) not in buffer:
buffer.add((k, v))
yield k, v
break
except RuntimeError as e:
if str(e) != 'dictionary changed size during iteration':
raise e
当字典被修改时,异常导致 for 循环重新启动,跳过 key-values 对已经产生,外循环在字典被改变时重新开始一切,并在 d 中的每个元素都被产生时中断.
您唯一需要注意的是,当返回每个项目并且生成器停止时,字典更改不会像代码现在那样重新启动它
EDIT 添加了哈希以捕获评论中提到的非常具体的案例
我想定义一个生成器函数,它可以遍历正在发生变化的字典对象。
def generator(d):
for k,v in sorted(d.items()):
yield (k,v)
d = {1:'a', 2:'x', 4:'m', 8:'d', 16:'f'}
i = generator(d)
print(next(i))
print(next(i))
del d[8]
print(next(i))
d[16] = "f"
d[32] = "z"
print(next(i))
print(next(i))
如果我执行上面的代码,我会得到:
(1, 'a')
(2, 'x')
(4, 'm')
(8, 'd')
(16, 'f')
所需的输出应该是:
(1, 'a')
(2, 'x')
(4, 'm')
(16, 'f')
(32, 'z')
我不明白的部分是当我将 d = {1:'a', 2:'x', 4:'m', 8:'d', 16:'f'}
分配给 i = generator(d)
时,字典已经添加到函数 generator(d)
中,并成为带有初始字典的生成器实例。因此,每当我使用 del
函数时,生成器都会以 d = {1:'a', 2:'x', 4:'m', 8:'d', 16:'f'}
作为参数保持不变。我将如何实现这一目标?
您需要按索引而不是按元素生成。您需要在每次 yield 之间重新生成列表以捕获更改。
def generator(d):
for i in range(len(d)):
dd = sorted(d.items())
if i < len(dd):
yield dd[i]
d = {1:'a', 2:'x', 4:'m', 8:'d', 16:'f'}
i = generator(d)
print(next(i))
print(next(i))
del d[8]
print(next(i))
d[16] = "f"
d[32] = "z"
print(next(i))
print(next(i))
输出
(1, 'a')
(2, 'x')
(4, 'm')
(16, 'f')
(32, 'z')
您需要处理异常以检查字典何时更改,并需要一个缓冲区来保存已生成的键。
def generator(d):
buffer = set()
while True:
old_hash = hash(frozenset(d.items()))
try:
for k, v in d.items():
if hash(frozenset(d.items())) != old_hash:
raise RuntimeError('dictionary changed size during iteration')
if (k, v) not in buffer:
buffer.add((k, v))
yield k, v
break
except RuntimeError as e:
if str(e) != 'dictionary changed size during iteration':
raise e
当字典被修改时,异常导致 for 循环重新启动,跳过 key-values 对已经产生,外循环在字典被改变时重新开始一切,并在 d 中的每个元素都被产生时中断.
您唯一需要注意的是,当返回每个项目并且生成器停止时,字典更改不会像代码现在那样重新启动它
EDIT 添加了哈希以捕获评论中提到的非常具体的案例