[英]Copied list passed to generator reflects changes made to original
在回答这个问题时,我偶然发现了一些意想不到的行为:
from typing import List, Iterable
class Name:
def __init__(self, name: str):
self.name = name
def generator(lst: List[Name]) -> Iterable[str]:
lst_copy = lst.copy()
for obj in lst_copy:
yield obj.name
修改传递给生成器的列表时,即使进行了复制,对原始列表的更改仍会反映:
lst = [Name("Tom"), Name("Tommy")]
gen = generator(lst)
lst[0] = Name("Andrea")
for name in gen:
print(name)
输出:
Andrea
Tommy
只需返回生成器表达式即可按预期工作:
def generator(lst: List[Name]) -> Iterable[str]:
return (obj.name for obj in lst.copy())
输出:
Tom
Tommy
为什么第一个生成器函数中的lst.copy()
没有按预期工作?
我认为通过添加一些额外的打印语句可以最好地理解这种行为:
def generator(lst: List[Name]) -> Iterable[str]:
print("Creating list copy...")
lst_copy = lst.copy()
print("Created list copy!")
for obj in lst_copy:
yield obj.name
lst = [Name("Tom"), Name("Tommy")]
print("Starting assignment...")
gen = generator(lst)
print("Assignment complete!")
print("Modifying list...")
lst[0] = Name("Andrea")
print("Modification complete!")
for name in gen:
print(name)
请注意,副本不会在分配时发生——它发生在列表被修改之后!
Starting assignment...
Assignment complete!
Modifying list...
Modification complete!
Creating list copy...
Created list copy!
Andrea
Tommy
在for
循环尝试提取元素之前,不会执行生成器主体中的任何内容。 由于此提取尝试发生在列表突变之后,因此突变反映在生成器的结果中。
在请求第一项之前,生成器的主体不会开始执行。 所以在这段代码中:
def generator(lst: List[Name]) -> Iterable[str]:
lst_copy = lst.copy()
for obj in lst_copy:
yield obj.name
lst = [Name("Tom"), Name("Tommy")]
gen = generator(lst)
lst[0] = Name("Andrea")
for name in gen:
print(name)
...首先,执行lst[0] = Name("Andrea")
。 然后,你有一个for
循环,它开始执行生成器。 那是执行lst_copy = lst.copy()
的时候,在lst[0]
分配之前进入已经太晚了。
生成器表达式有效,因为生成器的可迭代部分( lst.copy()
,最后一部分)必须在创建迭代器之前进行评估。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.