允许迭代突变的python生成器函数
Posted
技术标签:
【中文标题】允许迭代突变的python生成器函数【英文标题】:python generator function that is allowed to iterate over mutations 【发布时间】:2021-02-17 18:02:54 【问题描述】:我想定义一个生成器函数,它可以遍历正在发生变化的字典对象。
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'
作为其参数保持不变。我将如何实现这一目标?
【问题讨论】:
你不能安全地这样做。您可能会删除迭代器刚刚返回的项目,这意味着迭代器现在指向下一次,但无论如何都会推进其指针,或者您添加的项目可能比当前位置更早添加,并且从未见过。 问题是sorted(d.items())
返回一个不是d
的新字典。因此,当您 del d[8]
时,您的生成器不会受到影响,因为它正在遍历不是 d
的新字典。不知道如何解决您的问题。
@Loocid: sorted(d.items())
生成元组列表,而不是字典。但是,它仍然不是d
,并且仍然不受d
更改的影响。
请问物品需要分类吗?似乎键是唯一的,当其他东西发生变化时,无论如何都必须在其他具有更高键的elemnets之后输出更改。我的意思是让我们说,而不是d[16]
,您决定更改d[2] = 'u'
4 之后的输出,'m' 将是 2,'u 因为键值对已更改,所以排序目的是什么
@jimakr 项目不必排序。我之所以对它进行排序是因为我想获得不是每次都产生的最小键。
【参考方案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 为捕捉 cmets 中提到的一个非常具体的情况添加了哈希
【讨论】:
这不处理删除一个条目并插入另一个条目的情况。 对于这种情况,一个简单的哈希就足够了,但我会在中编辑它 这段代码有问题,它不会在每次产生时都重置集合。已编辑:我的错误是我没有在函数内看到打印语句 是的,抱歉忘记打印了,我编辑并添加了字典的哈希值,以检查是否在调用函数之前删除和添加值,使 python 检测不到字典中的更改 @jimakr 这会打印出字典中最小的键吗?不对吗?如果我每次都需要产生最小的键怎么办?【参考方案2】:您需要按索引而不是按元素产生。您需要在每个产量之间重新生成列表以捕获更改。
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')
【讨论】:
这太贵了,而且仍然实际上不起作用。仍然可以跳过条目,如果发生插入,它们可能会被重复计算。以上是关于允许迭代突变的python生成器函数的主要内容,如果未能解决你的问题,请参考以下文章