【问题标题】:Odd function closure behavior in generator生成器中的奇怪函数闭包行为
【发布时间】:2020-03-06 14:52:50
【问题描述】:

我遇到了一个我不理解的 python 行为。

这是一个简单的演示代码:

l = []
def ext_and_return_l(ext):
    l.append(ext)
    return l

extensions = [1, 2, 3, 4]
gen = map(ext_and_return_l, extensions)

现在说我想看看我的发电机的条款,我有选择。

先循环,给我我的期望:

for i in gen:
    print(i)

 [1]
 [1, 2]
 [1, 2, 3]
 [1, 2, 3, 4]

在再次运行演示代码后使用列表:

list(gen)

[[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]

我认为这与函数闭包绑定较晚有关,但我不了解这种情况下的确切机制,希望能在这里得到一些见解。

【问题讨论】:

  • 这不是一个闭包,真的,只是 l 是一个全局变量,所以每次调用 ext_and_return_l 都会修改同一个列表。
  • 如果你需要你期望的迭代器,使用list.copy,例如:def yield_list_copy(gen): """ >>> assert list(yield_list_copy(mk_gen())) == [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4]]""" for i in gen: yield i.copy()

标签: python function closures generator


【解决方案1】:

有一个全局列表l,每次调用ext_and_return_l 都会修改同一个列表。您使用map 懒惰地将ext_and_return_l 应用于extensions 的事实并不重要。

您的循环所做的只是在不同时间打印l 的值(i 只是一遍又一遍地重新绑定到同一个列表,而不是不同 列表)。为了更清楚地看到这一点,在循环中添加一行:

for i in gen:
    print(id(i))  # This will print the same value on each iteration.
    print(i)

【讨论】:

  • 谢谢,当然...我在错误的地方寻找答案!
【解决方案2】:

扩展@chepner 的评论:当您使用for 循环时

for i in gen:
    print(i)

您在追加后立即打印每个迭代。当你使用list时,它与:

results = []
for i in gen:
    results.append(i)
print(results)

这是因为返回的列表,正如@chepner 所提到的,是一个单一的全局实例。要进一步了解这一点,请尝试以下操作,看看您会得到什么:

l = []
results = []
results.append(l)
print(results)
l.append(1)
results.append(l)
print(results)

【讨论】:

  • 感谢您的澄清,这很有意义。
【解决方案3】:

@chepner 和 @cmac 已经说明了您观察到的行为的原因:您正在生成一个 single相同的引用事情

如果您正在寻找问题的解决方案...我猜想告诉我您正在寻找一个既作用(累积数据)和的生成器em> yields(累加器的不同阶段)。

也许这正是你的上下文所需要的,但如果我是你,我会考虑是否有一种不那么杂技的方式来实现你的目标。

如果您需要它,您可以通过在输出中添加 .copy() 从代码中获取您想要的内容:

l = []
def ext_and_return_l(ext):
    l.append(ext)
    return l.copy()  # or tuple(l) if you want to "freeze" the output sequence.

assert list(map(ext_and_return_tuple, extensions)) == [(1,), (1, 2), (1, 2, 3), (1, 2, 3, 4)]

【讨论】:

    猜你喜欢
    • 2019-08-07
    • 2016-03-19
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-30
    相关资源
    最近更新 更多