UnboundLocalError с операторами и функциональным программированием на python (методы работают нормально) - PullRequest
3 голосов
/ 23 апреля 2011

prog1.py:

def runf(f):
    f()

def main():
    l = [0]
    def f():
        l.append(1)
    runf(f)
    print(l)

main()

Дает мне (как и ожидалось):

[0, 1]

prog2.py:

def runf(f):
    f()

def main():
    l = [0]
    def f():
        l += [1] # <-- Only difference
    runf(f)
    print(l)

main()

Дает мне:

Traceback (most recent call last):
  File "prog2.py", line 11, in <module>
    main()
  File "prog2.py", line 8, in main
    runf(f)
  File "prog2.py", line 2, in runf
    f()
  File "prog2.py", line 7, in f
    l += [1]
UnboundLocalError: local variable 'l' referenced before assignment

Может кто-нибудь объяснить мне, что здесь происходит?

Примечание: это происходит как в python2, так и в python3.

Также я открыт для предложений поЛучшее название для этого вопроса.

1 Ответ

4 голосов
/ 23 апреля 2011

Ссылка на модель исполнения Python (раздел 4.1) гласит:

Если имя связано в блоке, это локальная переменная этого блока.

Что происходит, так это то, что l += [1] ради связывания эквивалентно l = l + [1], что означает l является связанным внутри f.Вот еще одна интересная ссылка на документ :

Присвоение объекта одной цели рекурсивно определяется следующим образом.

Если целью является идентификатор (имя):

  • Если имя не встречается в глобальной инструкции в текущем блоке кода: имя привязано к объекту в текущем локальном пространстве имен.
  • В противном случае: имяпривязан к объекту в текущем глобальном пространстве имен.

Здесь уместно предложение , в противном случае .Поскольку вы не объявили global l в области действия f и присвоили l, имя будет связано с локальным пространством имен f.Затем ссылка на него, неявно созданная l += [1], ссылается на переменную, которая еще не была определена.Отсюда UnboundLocalError.


PS global l вам, кстати, не поможет.В Python 3 есть оператор nonlocal для обработки таких случаев:

Нелокальный оператор заставляет перечисленные идентификаторы ссылаться на ранее связанные переменные в ближайшей охватывающей области.Это важно, потому что поведение привязки по умолчанию заключается в том, чтобы сначала выполнить поиск в локальном пространстве имен.Оператор позволяет инкапсулированному коду связывать переменные вне локальной области, кроме глобальной (модульной) области.

...