Может ли Python Closure хранить и изменять внешний скаляр? - PullRequest
0 голосов
/ 21 июня 2020

среда: Python 3.7.5

Я пытался понять закрытие в Python, поэтому я сделал следующие попытки:

Сначала я написал функцию, которая генерирует функции для изменить вектор, который я инициировал:

def outer(c=[0,0]):
    
    def inner(x):
        nx = c[0] + x[0]
        ny = c[1] + x[1]
        c[0] = nx
        c[1] = ny
        return c
    
    return inner

Вот результат, который я получил:

>>> p = outer()
>>> p([1,2])
[1, 2]
>>> p([1,2])
[2, 4]
>>> p([1,2])
[3, 6]

Он работал хорошо, внутренняя функция сохраняла вектор на каждом шаге, чтобы вектор мог постепенно меняться

Затем я написал аналогичную функцию, только изменив вектор c на скаляр:

def outer(c=0):
    
    def inner(x):
        nx = c + x
        c = nx
        return c
    
    return inner

Однако на этот раз я не могу ее запустить:

>>> p = outer()
>>> p(1)
Traceback (most recent call last):
  File "<pyshell#35>", line 1, in <module>
    p(1)
  File "C:/Users/thisi/Desktop/21345.py", line 5, in inner
    nx = c + x
UnboundLocalError: local variable 'c' referenced before assignment

Интересно, почему python не может найти переменную 'c' на этот раз? А почему первый случай сработал?

Ответы [ 2 ]

3 голосов
/ 21 июня 2020

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

def outer(c=0):

    def inner(x):
        nonlocal c   # <-- note `nonlocal`
        nx = c + x
        c = nx
        return c

    return inner

p = outer()
print( p(1) )
print( p(1) )

Выводит:

1
2
1 голос
/ 21 июня 2020

Если есть какие-либо операторы присваивания переменной внутри функции, то эта переменная находится в локальной области видимости функции (если не объявлено явно nonlocal).

Рассмотрим этот код:

def outer(c=0):
    
    def inner(x):
        if False: c = 0
        return c + x
    
    return inner

func = outer(3)
print(func(2))

Это даст UnboundLocalError, даже если присвоение c = 0 никогда не выполняется. Но если строка с присвоением полностью удалена, она будет запущена.

В примере со списком переменная c нигде не назначена в inner; он изменяется только с помощью присвоения позиции.

...