Каждый из next () и list () перебирает генератор с изменяемым объектом по-разному

1
def generator(dct):
    for i in range(3):
        dct['a'] = i
        yield dct

g = generator({'a': None})
next(g) # -> {'a': 0}
next(g) # -> {'a': 1}
next(g) # -> {'a': 2}

g = generator({'a': None})
list(g) # -> [{'a': 2}, {'a': 2}, {'a': 2}]

next итератор генератора, как я и ожидал. Однако list неожиданно повторяется. Я думаю, что он должен возвратить [{'a': 0}, {'a': 1}, {'a': 2}] так как next итерации без проблем.

Интересно, в чем разница.

(моя среда: python 3.6.5, conda 4.5.4, Windows)

Теги:

3 ответа

6
Лучший ответ

generator() генератора всегда дает один и тот же объект dict, только с изменениями, внесенными в него между вызовами. Если бы вы, скажем, изменили первый next(g) на x = next(g) а затем проверили x до и после вызова next(g) снова, вы увидите значение изменения x разных вызовов, потому что generator() всегда изменяет и дает один и тот же объект dict.

3

Они одинаково перебирают итератор, но вы проверяете их в разных точках. Поскольку dict изменчивы, и вы всегда yield один и тот же dict, вы должны ожидать все, что вы уступаете быть идентичными. В вашем первом примере вы смотрите на dict поскольку он меняется. Вместо этого

g = generator({'a': None})

a = next(g)
b = next(g)
c = next(g)

print(a, b, c)
# {'a': 2} {'a': 2} {'a': 2}
1

Вы просматриваете промежуточные результаты печати. Вы изменяете один словарь и обмениваетесь ссылками на него. Вы можете видеть промежуточные шаги, но это не значит, что результат отличается.

Сохраните ссылку на объект, возвращенный из next() и все они будут одинаковыми:

g = generator({'a': None})
first = next(g)
second = next(g)
third = next(g)
print(first, second, third) # -> {'a': 2}  {'a': 2}  {'a': 2}

first, second и third - все ссылки на один и тот же объект:

>>> first is second and second is third
True

Вы увидели бы то же самое, если вы сделали это в регулярном for цикла:

>>> results = []
>>> d = {'a': None}
>>> for i in range(3):
...     d['a'] = i
...     print(d)
...     results.append(d)
...
{'a': 0}
{'a': 1}
{'a': 2}
>>> print(results)
[{'a': 2}, {'a': 2}, {'a': 2}]
>>> all(d is elem for elem in results)  # all references in results are the same object
True

Цикл печатает объект словаря при изменении каждого шага. Список results содержит 3 ссылки на один и тот же объект, и каждый показывает одно и то же состояние после печати в конце.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню