Проблема с областью видимости Python

У меня есть тривиальный пример:


def func1():
    local_var = None

    def func(args):
        print args,
        print "local_var:", local_var

        local_var = "local"

    func("first")
    func("second")

func1()

Я ожидаю, что вывод будет:

first local_var: None
second local_var: local

Однако мой фактический результат:

first local_var:
Traceback (most recent call last):
  File "test.py", line 13, in 
    func1()
  File "test.py", line 10, in func1
    func("first")
  File "test.py", line 6, in func
    print "local_var:", local_var
UnboundLocalError: local variable 'local_var' referenced before assignment

Мое понимание правил области видимости python диктует, что это должно работать так, как ожидалось. У меня есть другой код, где это работает, как и ожидалось, но сокращение одного неработающего фрагмента кода до его тривиального случая выше также не работает. Так что я в тупике.


person Community    schedule 28.07.2009    source источник


Ответы (4)


Присвоение local_var в func делает его локальным по отношению к func, поэтому оператор print ссылается на эту "очень-очень локальную" переменную еще до того, как она будет назначена, как сказано в исключении. Как говорит jtb, в Python 3 вы можете решить эту проблему с помощью nonlocal, но из вашего кода, использующего операторы print, ясно, что вы работаете в Python 2. Традиционное решение в Python 2 состоит в том, чтобы убедиться, что присваивание не barename и, таким образом, не делает переменную более локальной, чем вы хотите, например:

def func1():
    local_var = [None]

    def func(args):
        print args,
        print "local_var:", local_var[0]

        local_var[0] = "local"

    func("first")
    func("second")

func1()

присваивание индексации не пустому имени и, следовательно, не влияет на локальность, и, начиная с Python 2.2, для вложенных внутренних функций вполне приемлемо ссылаться на переменные, которые являются локальными во внешних содержащих функциях, что все, что делает эта версия (назначение barenames — это другая проблема, чем ссылка на переменные).

person Alex Martelli    schedule 28.07.2009
comment
Спасибо! Это объясняет, почему иногда я получал ожидаемое поведение — когда я не переназначал свою локальную переменную внутри функции (в данном случае func()), все было в порядке. Акт назначения на него сделал его очень и очень локальным, как вы выразились. - person ; 28.07.2009

Стандартный способ решить эту проблему до версии 3.0:

def func1():
    local_var = [None]

    def func(args):
        print args,
        print "local_var:", local_var[0]

        local_var[0] = "local"

    func("first")
    func("second")

func1()
person cefstat    schedule 28.07.2009

Правила области видимости Python обсуждаются и объясняются в этом связанном вопросе:

Причина неинтуитивного поведения UnboundLocalError

person ire_and_curses    schedule 28.07.2009

До Python 3.0 функции не могли записывать неглобальные переменные во внешних областях. В Python3 появилось ключевое слово nonlocal, которое позволяет это сделать. Вы бы добавили nonlocal local_var вверху определения func(), чтобы получить ожидаемый результат. См. PEP 3104.

Если вы не работаете в Python 3, вам придется сделать переменную глобальной или каким-то образом передать ее в функцию.

person jtb    schedule 28.07.2009