Закрытие / вложенная функция Python завершается с ошибкой при присваивании внешнему массиву [duplicate]

1

Кажется, что закрытие функции python имеет проблемы, если присваивается обозначенный символ:

def outer():
    p = []
    def gen():
        def touch(e):
            if e[0] == 'add':
                p.append(e);
            elif e[0] == 'rem':
                p = [ x for x in p if not (x[1] == e[1]) ]
        return touch
    f = gen()
    for i in [["add","test1"],["add","test2"],["rem","test2"],["rem","test1"]]:
        f(i)      

outer();

и результат:

Traceback (most recent call last):
  File "b.py", line 22, in <module>
    outer();
  File "b.py", line 20, in outer
    f(i)      
  File "b.py", line 14, in touch
    p.append(e);
UnboundLocalError: local variable 'p' referenced before assignment

Если я просто для теста заменим:

 -       p = [ x for x in p if not (x[1] == e[1]logig is) ]                                                                                                                                
 +       a = [ x for x in p if not (x[1] == e[1]) ]                                                                                                                                

ошибка исчезает, однако код не то, что я хочу. Является ли вышеуказанное поведение ожидаемым с закрытием/вложенными функциями python? Нужно ли обертывать массив для изменения внутри объекта и просто вызвать функции?

С другой стороны, это работает:

class o():
    def __init__(self):
        self.p = []
    def add(self,e):
        self.p.append(e);
    def rem(self,e):
        self.p = [ x for x in self.p if not (x[1] == e[1]) ]

def outer():
    p = o()
    def gen():
        def touch(e):
            if e[0] == 'add':
                p.add(e);
            elif e[0] == 'rem':
                p.rem(e)
        return touch
    f = gen()
    for i in [["add","test1"],["add","test2"],["rem","test2"],["rem","test1"]]:
        f(i)      

outer();
  • 0
    Кажется, работает нормально, если вы определите р в сенсорной функции
  • 0
    Это не закрытие. programiz.com/python-programming/closure
Показать ещё 2 комментария
Теги:
closures

1 ответ

2
Лучший ответ

Поскольку вы назначаете p внутри touch, он становится локальной переменной в touch и эффективно "скрывает" все другие имена p в охватывающих областях. Чтобы сообщить Python, что вы действительно хотите ссылаться на p внутри outer, вы должны использовать nonlocal p, например:

def outer():
    p = []
    def touch(e):
        # The following line is required to refer to outer p
        nonlocal p
        if e[0] == 'add':
            p.append(e)
        elif e[0] == 'rem':
            p = [ x for x in p if not (x[1] == e[1]) ]
    for i in [["add","test1"],["add","test2"],["rem","test2"],["rem","test1"]]:
        touch(i)
outer()

Второй пример работает, потому что вы ссылаетесь на атрибут p в обоих случаях touch, а не на назначение (p =...).

См. nonlocal в справочной документации Python, справочную документацию для областей и PEP 3104, в которой был предложен nonlocal синтаксис. nonlocal существует только в Python 3, но есть обходное решение, если необходимо использовать Python 2.

  • 0
    Довольно неприятная ловушка. Спасибо за чаевые.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню