Дополнительно
Неожиданное поведение с условным выражением генератор
Я запустить кусок кода, который неожиданно дал логическая ошибка в одной части программы. При изучении раздела, я создал тестовый файл для проверки набор инструкций выполняется и выяснили, необычная ошибка, которая кажется очень странным.
Я тестировал этот простой код:
array = [1, 2, 2, 4, 5] # Original array
f = (x for x in array if array.count(x) == 2) # Filters original
array = [5, 6, 1, 2, 9] # Updates original to something else
print(list(f)) # Outputs filtered
А выход был:
>>> []
Да, ничего. Я ожидал понимания Filter, чтобы найти элементы в массив со счетчиком 2 и выходной, но я не'т получить, что:
# Expected output
>>> [2, 2]
Когда я закомментировал третьей строке, чтобы проверить его еще раз:
array = [1, 2, 2, 4, 5] # Original array
f = (x for x in array if array.count(x) == 2) # Filters original
### array = [5, 6, 1, 2, 9] # Ignore line
print(list(f)) # Outputs filtered
Вывод был правильным (Вы можете проверить это сами):
>>> [2, 2]
В один момент я вывести тип переменной Ф
:
array = [1, 2, 2, 4, 5] # Original array
f = (x for x in array if array.count(x) == 2) # Filters original
array = [5, 6, 1, 2, 9] # Updates original
print(type(f))
print(list(f)) # Outputs filtered
И я получил:
>>> <class 'generator'>
>>> []
Почему обновление списка в Python изменение выходной переменной генератор? Это кажется мне очень странным.
57
8
В Python'выражений генератор с позднего связывания (см. ОПТОСОЗ 289 -- выражения генератор) (что других ответов называют "ленивый") смотрите:
Это означает, что он только оценивает крайними " за " при создании генератора выражений. Так оно, собственно, *персонализация стоимость с именем
массив
в "выражения"в в массив (на самом деле она'с обязательным эквивалентом
ИТЭР(массив)на данный момент). Но когда вы перебирать генератор
если в массиве.количество вызововна самом деле относится к тому, что в настоящее время называется
массив`.Поскольку она'ов на самом деле
список
немассив
я изменил имена переменных в остальном ответ будет более точный.В первом случае "список" вы перебрать и
список
считать будут разные. Это's, как если бы вы использовали:Так что вы проверьте каждый элемент в "список 1", если ее рассчитывать в список2-это два.
Вы можете легко убедиться в этом, изменив второй список:
Если это повторяется по первому списку и в списке он бы'вэ вернулся
[2, 2]
(потому что первый список содержит две2
). Если это повторяется снова и насчитал в списке второй выход должен быть[1, 1]
. Но поскольку он проходит по первому списку (с одним1
), но чеки из второго списка (который содержит два 1-х) выход только один1
.Решение с помощью генератора функции
Есть несколько возможных решений, я вообще предпочитаю не использовать "в выражениях генератора" если они не'т итерации сразу. Простая функция генератора будет достаточно, чтобы сделать его работать правильно:
И затем использовать его как это:
Обратите внимание, что Пеп (см. ссылку выше) также говорится, что на что-то более сложное полное описание генератора является предпочтительным.
Лучшим решением, используя функциональный генератор с счетчиком
Лучшее решение (избегая квадратичное поведение во время выполнения, потому что вы перебирать весь массив для каждого элемента в массиве) будет рассчитывать (сборники
.Счетчик
) элементы один раз, а затем выполните поиск в постоянное время (в результате в линейном времени):Приложение: используя подкласс, чтобы "визуализация" Что происходит, и когда это произойдет
Это'ы довольно легко создать "список" подкласс, который печатает при конкретных методов, таким образом, можно убедиться, что это действительно работает.
В этом случае я просто переопределить методИТЭР
и
count` потому что я'м интересно за какой список выражений генератора проходит и в каком списке он рассчитывает. Метод тела на самом деле просто делегировать суперкласс и что-то напечатать (так как он использует "супер" без аргументов и F-строки, у меня 3.6, но это должно быть легко адаптировать и для других версий Python):Это простой подкласс только печати, когда
__ИТЭР__
и методграф
называются:Как уже упоминалось генераторов Python лентяи. Когда эта строка выполняется:
ничего не произойдет, пока. Вы'вэ просто объявлена как функция генератора F будет работать. Массив не посмотрел еще. Затем вы создаете новый массив, который заменяет первый, и, наконец, когда вы называете
генератор теперь нужны фактические значения и начинает тянуть их из генератора Ф. Но на данный момент, выбора уже относится ко второму, так что вы получите пустой список.
Если вам нужно переназначить список, и можете'т использовать другую переменную, чтобы удержать его, рассмотреть вопрос о создании списка вместо генератора во второй строке:
Другие уже объяснил причину проблемы - генератор привязки к имени
массив
локальной переменной, а не значение.Наиболее подходящие для Python решение, безусловно, понимание списка:
** Однако, если есть какая-то причина, что вы Don'т хотите, чтобы создать список, вы можете же силу охвата закрыть за
массив
:Что'ы происходит здесь заключается в том, что "лямбда" захватывает ссылкой на
массив
в то время, когда строка выполняется, убедившись, что генератор не видит переменную вы ожидаете, даже если переменная позднее пересмотрены.Обратите внимание, что это все-таки привязывает к переменная (ссылка), а не значение**, так, например, следующий будет печатать
[2, 2, 4, 4]
:Это обычная картина в некоторых языках, но это's не очень подходящие для Python, так что только действительно имеет смысл, если там's очень веские причины не использовать список понимание (например, если
массив
очень долго, или используется во вложенном генератора понимание, и вы'вновь обеспокоен памяти).Вы не используете генератор правильно, если это основное применение этого кодекса. Использовать список понимание вместо понимания генератора. Просто замените скобок со скобками. Он возвращает список, если вы не'т знаю.
Вы получаете этот ответ из-за особенностей генератора. Вы'вновь зовет генератора, когда он'т содержание будет оценено
[]
Генераторы ленивы, они выиграли'т быть оценено, пока вы перебираете их. В этом случае, что's в момент создания
список
с генератором в качестве входных, в "печать".Коренной причиной проблемы является то, что генераторы ленивы; переменные вычисляются каждый раз:
``
Он перебирает оригинальный список и оценивает состояние с текущим списком. В данном случае, 4 дважды встречается в новом списке, заставляя его появиться в результате. Он появляется только один раз в результате, потому что он появился только один раз в первоначальном списке. 6С дважды появляются в списке новых, но никогда не появляются в старом списке и поэтому не показано.
Полная функция самоанализа для любознательных (строка с комментарием является важной чертой):
``
3 6 LOAD_CLOSURE 0 (число) 9 LOAD_CLOSURE 1 (текущие) 12 BUILD_TUPLE 2 15 LOAD_CONST 1 (<код объекта В 0x02DD36B0, файл "<pyshell#17>”, в строке 3>) 18 LOAD_CONST 2 ('е.<местные жители и GT;.') 21 MAKE_CLOSURE 0 24 LOAD_DEREF 1 (текущие) 27 GET_ITER 28 CALL_FUNCTION 1 (1 позиционная, 0 пара ключевых слов) 31 STORE_FAST 3 (фильтрованное)
4 34 LOAD_FAST 1 (новый) 37 STORE_DEREF 1 (текущие)
5 40 LOAD_GLOBAL 0 (список) 43 LOAD_FAST 3 (фильтрованное) 46 CALL_FUNCTION 1 (1 позиционная, 0 пара ключевых слов) 49 RETURN_VALUE
Еще раз повторю, что список будет повторяться раз загружается только. Любое закрытие в условие или выражение, впрочем, загружаются из внешней области видимости каждой итерации. Они не сохраняются в постоянном.
Лучшее решение для вашей проблемы было бы создание новой переменной, ссылающейся на оригинальный список и использовать в выражениях генератора,.
Генераторы ленивый и вновь определенными
массив
используется, когда вы исчерпаете свой генератор после переопределения. Таким образом, вывод правильный. Быстро исправить это, чтобы использовать список понимание, заменив круглые скобки()
в скобках[]
.Перейти к как лучше написать вашей логике, считая значение в цикле имеет квадратичную сложность. Для алгоритм, который работает за линейное время, можно использовать коллекции
.Счетчик
для подсчета значений, и сохранять копию исходного списка:Обратите внимание на вторую версию не'т даже требуют `old_array и полезно, если нет необходимости поддерживать порядок следования значений в исходном массиве.
Оценка генератор есть "ленивый" и ... это не'т получить выполняется, пока вы реализовать его с соответствующей ссылкой. С вашей линии:
Еще раз взгляните на свой выход с типом "е": что объект является генератор, а не последовательность. Это'ы ждут, чтобы быть использованы, итератор сортов.
Ваш генератор это'т оценивала, пока вы не начнете требующие значения. В этот момент, он использует доступные значения на тот момент, не точка, в которой он был определен.
Код "и заставить его работать на"
Это зависит от того, что вы подразумеваете под "и заставить его работать на". Если вы хотите,
Ф
, чтобы быть отфильтрованный список, а затем использовать список, а не генератора: