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

Закрытие python с назначением внешней переменной внутри внутренней функции

У меня есть эта часть кода:

#!/usr/bin/env python

def get_match():
  cache=[]
  def match(v):
    if cache:
      return cache
    cache=[v]
    return cache
  return match
m = get_match()
m(1)

если я запустил его, он говорит:

UnboundLocalError: local variable 'cache' referenced before assignment

но если я это сделаю:

#!/usr/bin/env python

def get():
  y = 1
  def m(v):
    return y + v
  return m

a=get()
a(1)

выполняется.

Есть ли что-то со списком? или неправильная организация кода?

4b9b3361

Ответ 1

Проблема в том, что переменная cache не входит в область соответствия функции. Это не проблема, если вы только хотите прочитать ее, как в своем втором примере, но если вы ее назначаете, python интерпретирует ее как локальную переменную. Если вы используете python 3, вы можете использовать ключевое слово nonlocal для решения этой проблемы - для python 2 нет простого обходного пути, к сожалению.

def f():
    v = 0

    def x():
        return v    #works because v is read from the outer scope

    def y():
        if v == 0:  #fails because the variable v is assigned to below
            v = 1

    #for python3:
    def z():
        nonlocal v  #tell python to search for v in the surrounding scope(s)
        if v == 0:
            v = 1   #works because you declared the variable as nonlocal

Проблема несколько с глобальными переменными - вам нужно использовать global каждый раз, когда вы назначаете глобальную переменную, но не для ее чтения.

Краткое объяснение причин этого: Интерпретатор python компилирует все функции в специальный объект типа function. Во время этой компиляции он проверяет все локальные переменные, которые создает функция (для сбора мусора и т.д.). Эти имена переменных сохраняются внутри объекта функции. Поскольку совершенно законно "затенять" внешнюю переменную области (создать переменную с тем же именем), любая переменная, которая назначена и которая явно не объявлена ​​как global (или nonlocal в python3), считается быть локальной переменной.

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

Ответ 2

Доступ к переменной отличается от ее назначения.

У вас похожая ситуация с глобальными переменными. Вы можете получить к ним доступ в любой функции, но если вы попытаетесь назначить его без оператора global, он будет обновлять его в локальном контексте.

К сожалению, для локальных функций нет эквивалента оператора global, но вы можете обойти обновление, заменив

cache=[v]

с:

cache[:] = [v]

Ответ 3

Так как Python видит cache=[v] - присваивание cache, он рассматривает его как локальную переменную. Таким образом, ошибка довольно разумная - локальная переменная cache не была определена до ее использования в инструкции if.

Вероятно, вы хотите записать его как:

def get_match():
  cache=[]
  def match(v):
    if cache:
      return cache
    cache.append(v)
    return cache
  return match
m = get_match()
m(1)

Очень рекомендуемые чтения: Модель исполнения - Именование и привязка и PEP 227 - Статические вложенные области

Ответ 4

Заменить

cache=[]
def match(v):

с

def match(v,cache=[])

Объяснение: Ваш код объявляет cache как переменную get_match, которую возвращенный match(v) ничего не знает (из-за следующего назначения). Однако вы хотите, чтобы cache был частью пространства имен match.

Я знаю, что "злонамеренный" пользователь может переопределить кеш, но это их собственная ошибка. Если это проблема, альтернатива такова:

def match(v):
     try:
         if cache:
             return cache
     except NameError:
         cache = []
     ...

(см. здесь)