Я обнаружил, что размахиваю молотком понимания списка - PullRequest
8 голосов
/ 17 марта 2012

... и каждый цикл for выглядел как понимание списка.

Вместо:

for stuff in all_stuff:
    do(stuff)

Я делал (не назначая никому список):

[ do(stuff) for stuff in all_stuff ]

Это обычный шаблон, который можно найти в list-comp how-to . 1) ОК, так что ничего страшного, верно? Неправильно. 2) Разве это не просто стиль кода? Супер неправильно.

1) Да, это было неправильно. Как указывает NiklasB, HowTos состоит в создании нового списка.

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

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

Итак, вот мой вопрос. Если бы я делал это в очень длительном процессе, где потреблялось много данных, продолжал ли бы этот «список» потреблять мою память до тех пор, пока он меня не отпустил? Когда сборщик мусора заберет память? После того как область, в которую встроен этот список, потеряна?

Я думаю, да, это будет продолжать поглощать мою память. Я не знаю, как работает сборщик мусора python, но я бы рискнул сказать, что этот список будет существовать до тех пор, пока не будет вызван последний next для all_stuff.

EDIT.

Суть моего вопроса передается гораздо чище В этом вопросе (спасибо за ссылку Никлас)

Ответы [ 5 ]

6 голосов
/ 17 марта 2012

Если бы я делал это в очень длительном процессе, где потреблялось много данных, продолжал бы этот «список» просто потреблять мою память, пока меня не отпустили?

Абсолютно.

Когда сборщик мусора заберет память обратно?После того как область, в которую встроен этот список, потеряна?

CPython использует подсчет ссылок, так что это наиболее вероятный случай.Другие реализации работают по-другому, поэтому не рассчитывайте на это.

Спасибо Карлу за то, что он указал, что из-за сложных механизмов управления памятью, используемых CPython, это не означает, что памятьсразу после этого вернулся в ОС.

Я не знаю, как работает сборщик мусора python, но я бы рискнул сказать, что этот список будет существовать до тех пор, пока не будет вызван последний следующий вызов для all_stuff.

Я не думаю, что любой сборщик мусора работает так.Обычно они помечают и убирают, поэтому может пройти довольно много времени, прежде чем список будет подвергнут сборке мусора.

Это распространенный шаблон, встречающийся в инструкциях по компиляции списка.

Абсолютно нет.Дело в том, что вы повторяете список с целью что-то сделать с каждым элементом (do вызывается, потому что это побочные эффекты ).Во всех примерах List-comp HOWTO список повторяется, чтобы создать новый список на основе элементов старого.Давайте рассмотрим пример:

# list comp, creates the list [0,1,2,3,4,5,6,7,8,9]
[i for i in range(10)]

# loop, does nothing
for i in range(10):
    i  # meh, just an expression which doesn't have an effect

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

Кстати, если вы действительно хотите записать этот цикл в одну строку, используйте потребитель генератора, такой как deque.extend.В этом простом примере это будет немного медленнее, чем простой цикл for:

>>> from collections import deque
>>> consume = deque(maxlen=0).extend
>>> consume(do(stuff) for stuff in all_stuff)
3 голосов
/ 17 марта 2012

Попробуйте вручную выполнить GC и сбросить статистику.

gc.DEBUG_STATS

Печать статистики во время сбора.Эта информация может быть полезна при настройке частоты сбора.

ОТ

http://docs.python.org/library/gc.html

2 голосов
/ 17 марта 2012

Если вам нравится эта идиома, do возвращает то, что всегда оценивает как True или False и рассматривает аналогичную альтернативу без уродливых побочных эффектов, вы можете использовать выражение генератора в сочетании с any или all.

Для функций, которые возвращают ложные значения (или не возвращают):

any(do(stuff) for stuff in all_stuff)

Для функций, которые возвращают истинные значения:

all(do(stuff) for stuff in all_stuff)
2 голосов
/ 17 марта 2012

CPython GC будет пожинать его, если на него нет ссылок вне цикла.Jython и IronPython следуют правилам базовых сборщиков мусора.

0 голосов
/ 17 марта 2012

Я не знаю, как работает сборщик мусора python, но я бы рискнул сказать, что этот список будет существовать до тех пор, пока последний следующий вызов не будет вызван для all_stuff.

Хорошо,конечно, так и будет, поскольку вы создаете список, который будет иметь одинаковое количество элементов all_stuff.Переводчик не может отказаться от списка до его завершения, не так ли?Вы можете вызвать gc.collect между одним из этих циклов и другим, но каждый из них будет полностью построен до того, как его можно будет восстановить.

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

(do_something(i) for i in xrange(1000))

Однако вам все равно придется каким-то образом "гасить" этот генератор ...

...