允许对突变进行迭代的python生成器功能

时间:2020-11-05 02:17:44

标签: python generator iterable

我想定义一个生成器函数,该函数可以迭代正在变异的字典对象。

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'}作为其参数保持不变。我将如何实现这一目标?

2 个答案:

答案 0 :(得分:1)

您需要处理异常情况以检查何时更改了字典,并需要使用缓冲区来保持键已经产生。

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循环重新启动,跳过已经产生的键值对,外循环会在更改字典时重新启动所有内容,并在d中的每个元素产生后中断。

您唯一需要了解的是,当每一项都返回并且生成器停止时,对字典的更改不会像现在的代码那样重新启动它

EDIT添加了哈希,以捕获注释中提到的非常具体的情况

答案 1 :(得分:-1)

您需要按索引而不是按元素来产生。您需要重新生成每个产量之间的列表以捕获变化。

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')