Почему UnboundLocalError возникает во второй переменной плоского понимания? - PullRequest
0 голосов
/ 09 октября 2018

Я ответил на вопрос здесь: список понимания в python2 работает нормально, но я получаю ошибку в python3

Ошибка OP использовала те же переменные для максимального диапазона и индексов:

x = 12
y = 10
z = 12
n = 100

ret_list = [ (x,y,z) for x in range(x+1) for y in range(y+1) for z in range(z+1) if x+y+z!=n ]

Это только ошибка Python-3, относящаяся к областям, которые были добавлены в понимание, чтобы избежать переменных, определенных здесь как «утечка».Изменение имен переменных исправляет это.

Ошибка:

UnboundLocalError: local variable 'y' referenced before assignment

, потому что внешняя, глобальная y скрыта локальной областью действия.

Мой вопрос:почему я получаю ошибку на y, а не на z или x?

РЕДАКТИРОВАТЬ: Если я удаляю цикл на x, ошибка перемещается в z:

>> ret_list = [ (x,y,z) for y in range(y+1) for z in range(z+1) if x+y+z!=n ]
UnboundLocalError: local variable 'z' referenced before assignment

Если я просто сделаю один цикл:

ret_list = [ (x,y,z) for y in range(y+1) if x+y+z!=n ]

, это сработает.Поэтому я подозреваю, что первая функция range оценивается перед всеми другими выражениями, что оставляет значение x без изменений.Но точную причину еще предстоит выяснить.Использование Python 3.4.3.

1 Ответ

0 голосов
/ 09 октября 2018

Это поведение (неявно) описано в справочной документации (выделено мной).

Однако, кроме итеративного выражения в крайнем левом предложении 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 вызвал ошибку.

Существует сообщение об ошибке здесь , в котором упоминается такое поведение.Это было решено как «не ошибка» по той причине, что желательно, чтобы списочные выражения и выражения генератора имели одинаковое поведение.

...