Это поведение (неявно) описано в справочной документации (выделено мной).
Однако, кроме итеративного выражения в крайнем левом предложении for
,Понимание выполняется в отдельной неявно вложенной области.Это гарантирует, что имена, назначенные в целевом списке, не «просачиваются» в охватывающую область.
Итерируемое выражение в крайнем левом предложении for
оценивается непосредственно в охватывающей области и затем передаетсяв качестве аргумента для имплицитно [sic] вложенной области. Последующие предложения for
и любое условие фильтра в крайнем левом предложении for
не могут быть оценены во внешней области видимости, поскольку они могут зависеть от значений, полученных из самой левой итерируемой,Например: [x*y for x in range(10) for y in range(x, x+10)]
.
Это означает, что:
list_ = [(x, y) for x in range(x) for y in range(y)]
эквивалентно:
def f(iter_):
for x in iter_:
for y in range(y):
yield x, y
list_ = list(f(iter(range(x))))
в качестве имени x
в дляКрайняя левая итерация читается во вложенной области, а не во вложенной области, тогда между этими двумя вариантами использования x
нет конфликта имен.То же самое не верно для y
, поэтому именно здесь происходит UnboundLocalError
.
Относительно того, почему это происходит: понимание списка является более или менее синтаксическим сахаром для list(<generator expression>)
,поэтому он будет использовать тот же путь кода, что и выражение генератора (или, по крайней мере, вести себя так же).Выражения генератора оценивают итеративное выражение в крайнем левом предложении for
, чтобы сделать обработку ошибок, когда выражение генератора несколько более разумно.Рассмотрим следующий код:
y = None # line 1
gen = (x + 1 for x in range(y + 1)) # line 2
item = next(gen) # line 3
y
- явно неправильный тип, поэтому сложение вызовет TypeError
.Путем немедленной оценки range(y + 1)
эта ошибка типа возникает в строке 2, а не в строке 3. Таким образом, легче диагностировать, где и почему возникла проблема.Если бы это произошло в строке 3, то вы могли бы ошибочно предположить, что именно оператор x + 1
вызвал ошибку.
Существует сообщение об ошибке здесь , в котором упоминается такое поведение.Это было решено как «не ошибка» по той причине, что желательно, чтобы списочные выражения и выражения генератора имели одинаковое поведение.