繁体   English   中英

传递给生成器的复制列表反映了对原始列表所做的更改

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM