2013-08-16 2 views
4

Когда я запускаю этот код, я получаю этот результат:Закрытие в python?

15 
15 

Я ожидаю, что выход должен быть

15 
17 

, но это не так. Вопрос в том, почему?

def make_adder_and_setter(x): 
    def setter(n): 
     x = n 

    return (lambda y: x + y, setter) 

myadder, mysetter = make_adder_and_setter(5) 
print myadder(10) 
mysetter(7) 
print myadder(10) 
+0

@Wooble: В конце концов, я потушил. :-) –

ответ

8

Python 2.x имеет ограничение по синтаксису, которое не позволяет фиксировать переменную в режиме чтения/записи.

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

  1. переменная глобальная и была объявлена ​​так с global x
  2. переменная является локальным из функция

более конкретно, это исключено, что переменная является локальной из функции вшита сферы

Это было заменено на Python 3.x с добавлением объявления nonlocal.Ваш код будет работать, как ожидается, в Python 3, изменив его

def make_adder_and_setter(x): 
    def setter(n): 
     nonlocal x 
     x = n 

    return (lambda y: x + y, setter) 

Среда Python 2.x может обрабатывать чтения-записи сомкнулась над переменной на уровне байт-кода, однако ограничение в синтаксисе, что компилятор принимает.

Вы можете увидеть lisp-компилятор, который генерирует байт-код python напрямую, что создает закрытие сумматора с записанным состоянием чтения-записи at the end of this video. Компилятор может генерировать байт-код для Python 2.x, Python 3.x или PyPy.

Если вам нужно замкнутому над изменяемым состоянием в Python 2.x трюк заключается в использовании списка:

def make_adder_and_setter(x): 
    x = [x] 
    def setter(n): 
     x[0] = n 

    return (lambda y: x[0] + y, setter) 
+0

спасибо, теперь я понимаю проблему –

13

Вы устанавливаете локальных переменныеx в функции setter(). Назначение имени в функции обозначает его как локальное, если вы специально не укажете компилятору Python в противном случае.

В Python 3, вы можете явно пометить x как нелокального используя nonlocal ключевое слово:

def make_adder_and_setter(x): 
    def setter(n): 
     nonlocal x 
     x = n 

    return (lambda y: x + y, setter) 

Теперь x отмечен в качестве свободной переменной и ищется в окружающем объеме, а не когда назначен.

В Python 2 вы не можете отметьте Python локальным как таковым. Единственный другой вариант, который у вас есть, - это отметка x как global. Вам придётся прибегнуть к трюкам, в которых вы изменяете значения, содержащиеся в изменяемом объекте, который живет в окружении.

Атрибут на Функция setter будет работать, например; setter является локальным для make_adder_and_setter() сферы, атрибутов этого объекта будет видны всем, что имеет доступ к setter:

def make_adder_and_setter(x): 
    def setter(n): 
     setter.x = n 
    setter.x = x 

    return (lambda y: setter.x + y, setter) 

Еще одна хитрости заключается в использовании изменяемого контейнера, например, список:

def make_adder_and_setter(x): 
    x = [x] 
    def setter(n): 
     x[0] = n 

    return (lambda y: x[0] + y, setter) 

В обоих случаях вы: не, назначая местное имя; в первом примере используется назначение атрибутов на объекте setter, второе изменяет список x, а не присваивается самому x.

2

Ваша внутренняя def setter(n) функция определяет свою собственную локальную переменную x. Это скрывает другую переменную x, которая была параметром make_adder_and_setter (делает отверстие в области). Таким образом, функция setter не имеет побочного эффекта. Он просто устанавливает значение внутренней локальной переменной и выходит.

Возможно, вам будет ясно, если вы попробуете код ниже. Он делает то же самое, просто использует имя z вместо x.

def make_adder_and_setter(x): 
    def setter(n): 
     z = n 

    return (lambda y: x + y, setter) 

myadder, mysetter = make_adder_and_setter(5) 
print myadder(10) 
mysetter(7) 
print myadder(10) 
Смежные вопросы