Подтвердить что ты не робот

Почему одна переменная класса не определена в понимании списка, а другая?

Я просто прочитал ответ на этот вопрос: Доступ к переменным класса из понимания списка в определении класса

Это помогает мне понять, почему следующий код приводит к NameError: name 'x' is not defined:

class A:
    x = 1
    data = [0, 1, 2, 3]
    new_data = [i + x for i in data]
    print(new_data)

NameError происходит потому, что x не определен в специальной области для понимания списка. Но я не могу понять, почему следующий код работает без ошибок.

class A:
    x = 1
    data = [0, 1, 2, 3]
    new_data = [i for i in data]
    print(new_data)

Я получаю вывод [0, 1, 2, 3]. Но я ожидал эту ошибку: NameError: name 'data' is not defined, потому что я ожидал точно так же, как в предыдущем примере, имя x не определено в области видимости списка, аналогично, имя data также не будет определено в понимании списка сфера.

Не могли бы вы помочь мне понять, почему x не определен в области понимания списка, но data есть?

4b9b3361

Ответ 1

data является источником понимания списка; это единственный параметр, который передается в созданную вложенную область.

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

>>> def foo():
...     return [i for i in data]
... 
>>> dis.dis(foo)
  2           0 LOAD_CONST               1 (<code object <listcomp> at 0x105390390, file "<stdin>", line 2>)
              3 LOAD_CONST               2 ('foo.<locals>.<listcomp>')
              6 MAKE_FUNCTION            0
              9 LOAD_GLOBAL              0 (data)
             12 GET_ITER
             13 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             16 RETURN_VALUE

Объект кода <listcomp> вызывается как функция, а iter(data) передается в качестве аргумента (CALL_FUNCTION выполняется с 1 позиционным аргументом, результатом GET_ITER).

Объект кода <listcomp> ищет этот один аргумент:

>>> dis.dis(foo.__code__.co_consts[1])
  2           0 BUILD_LIST               0
              3 LOAD_FAST                0 (.0)
        >>    6 FOR_ITER                12 (to 21)
              9 STORE_FAST               1 (i)
             12 LOAD_FAST                1 (i)
             15 LIST_APPEND              2
             18 JUMP_ABSOLUTE            6
        >>   21 RETURN_VALUE

Вызов LOAD_FAST относится к первому и только позиционному аргументу, переданному; он не называется здесь, потому что никогда не было определения функции, чтобы дать ему имя.

Любые дополнительные имена, используемые в понимании списка (или, например, для установки или определения dict или выражения генератора), являются либо локальными, либо закрывающими, либо глобальными, а не параметрами.