У понимания есть некоторые неожиданные взаимодействия с областью видимости. Это ожидаемое поведение?
У меня есть способ:
def leave_room(self, uid):
u = self.user_by_id(uid)
r = self.rooms[u.rid]
other_uids = [ouid for ouid in r.users_by_id.keys() if ouid != u.uid]
other_us = [self.user_by_id(uid) for uid in other_uids]
r.remove_user(uid) # OOPS! uid has been re-bound by the list comprehension above
# Interestingly, it's rebound to the last uid in the list, so the error only shows
# up when len > 1
Рискуя ныть, это серьезный источник ошибок. Когда я пишу новый код, я просто иногда нахожу очень странные ошибки из-за повторной привязки - даже сейчас, когда я знаю, что это проблема. Мне нужно сделать правило вроде «всегда предварять временные переменные в понимании списка с подчеркиванием», но даже это не является надежным.
Тот факт, что есть случайное ожидание с бомбой замедленного действия, сводит на нет всю приятную «простоту использования» понимания списков.
python
binding
list-comprehension
Джабаву Адамс
источник
источник
for
переменным -loop иfor
-loops для утечки . Так что это не было явным, но было заявлено неявно.Ответы:
Понимание списков приводит к утечке переменной управления циклом в Python 2, но не в Python 3. Вот Гвидо ван Россум (создатель Python), объясняющий историю этого:
источник
break
- но не имеет отношения к пониманию. Я вспоминаю некоторые обсуждения comp.lang.python, в которых люди хотели назначать переменные в середине выражения. Менее безумный путь обнаружено одним значения для положения , например.sum100 = [s for s in [0] for i in range(1, 101) for s in [s + i]][-1]
, но ему просто нужна переменная, локальная для понимания, и она так же хорошо работает в Python 3. Я думаю, что «утечка» была единственным способом сделать переменную видимой вне выражения. Все согласились, что эти методы ужасны :-)Да, понимание списков «пропускает» свою переменную в Python 2.x, как и в циклах for.
Оглядываясь назад, это было признано ошибкой, и ее удалось избежать с помощью выражений генератора. РЕДАКТИРОВАТЬ: как отмечает Мэтт Б., этого также удалось избежать, когда синтаксисы набора и понимания словаря были перенесены из Python 3.
Поведение списков должно быть таким же, как в Python 2, но полностью исправлено в Python 3.
Это означает, что во всех:
x
всегда локальна для выражения в то время как эти:в Python 2.x все утечки
x
переменной в окружающую область видимости.ОБНОВЛЕНИЕ для Python 3.8 (?) : PEP 572 представит
:=
оператор присваивания, который намеренно выходит за пределы понимания и выражений генератора! По сути, это мотивировано двумя вариантами использования: захват «свидетеля» из функций раннего завершения, таких какany()
иall()
:и обновление изменяемого состояния:
См. Приложение B для точного определения объема. Переменная назначается в ближайшем окружении
def
илиlambda
, если эта функция не объявляет ееnonlocal
илиglobal
.источник
Да, присваивание происходит там, как в
for
цикле. Никакой новой области не создается.Это определенно ожидаемое поведение: в каждом цикле значение привязывается к указанному вами имени. Например,
Как только это распознано, кажется, что этого достаточно легко избежать: не использовать существующие имена для переменных в пределах понимания.
источник
Интересно, что это не влияет на словарь или понимание набора.
Однако это было исправлено в 3, как указано выше.
источник
какое-то обходное решение для python 2.6, когда такое поведение нежелательно
источник
В python3, находясь в понимании списка, переменная не изменяется после выхода из области видимости, но когда мы используем простой цикл for, переменная переназначается вне области видимости.
i = 1 print (i) print ([i in range (5)]) print (i) Значение i останется только 1.
Теперь просто используйте цикл for, значение i будет переназначено.
источник