Поведение нечетных функций в генераторе - PullRequest
1 голос
/ 06 марта 2020

Я столкнулся с 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]]

Я полагаю, что это связано с поздним связыванием закрытия функций, но я не понимаю точного механизма в этом случае и был бы признателен за понимание.

Ответы [ 3 ]

1 голос
/ 06 марта 2020

Существует один глобальный список l, и каждый вызов ext_and_return_l изменяет этот же список. Тот факт, что вы используете map для ленивого применения ext_and_return_l к extensions, не имеет значения.

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

for i in gen:
    <b>print(id(i))</b>  # This will print the same value on each iteration.
    print(i)
0 голосов
/ 10 марта 2020

Причина для поведения, которое вы наблюдаете, уже указана @chepner и @ cma c: вы генерируете одиночную ссылку на то же самое .

Если вы ищете решение проблемы, хотя ... Я дико догадываюсь, что вы идете за генератором, который одновременно действует (накапливает данные) и дает (разные ступени аккумулятора).

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

Однако, если вам это нужно, вы можете получить то, что хотите, из своего кода, добавив .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)]
0 голосов
/ 06 марта 2020

Чтобы расширить комментарий @ chepner: при использовании for l oop

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)
...