функция - присвоение функции переменной python




Локальные переменные в вложенных функциях Python (2)

Вложенная функция ищет переменные из родительской области при ее выполнении, а не при определении.

Тело функции скомпилировано, а «свободные» переменные (не определенные в самой функции по назначению) проверяются, а затем привязываются как замыкающие ячейки к функции, причем код использует индекс для ссылки на каждую ячейку. pet_function образом, pet_function имеет одну свободную переменную ( cage ), которая затем ссылается через ячейку замыкания, индекс 0. Сама закрытие указывает на локальную переменную cage в функции get_petters .

Когда вы на самом деле вызываете функцию, это закрытие затем используется для просмотра значения cage в окружающей области в момент вызова функции . Здесь кроется проблема. К тому моменту, когда вы вызываете свои функции, функция get_petters уже выполняется, вычисляя ее результаты. Локальная переменная cage в какой-то момент во время этого выполнения была назначена каждой из строк 'cow' , 'dog' и 'cat' , но в конце этой функции cage содержит последнее значение 'cat' . Таким образом, когда вы вызываете каждую из динамически возвращенных функций, вы получаете значение 'cat' .

Обход - это не полагаться на закрытие. Вместо этого вы можете использовать частичную функцию , создать новую область функций или привязать переменную как значение по умолчанию для параметра ключевого слова .

  • Пример частичной функции, используя functools.partial() :

    from functools import partial
    
    def pet_function(cage=None):
        print "Mary pets the " + cage.animal + "."
    
    yield (animal, partial(gotimes, partial(pet_function, cage=cage)))
    
  • Пример создания новой области:

    def scoped_cage(cage=None):
        def pet_function():
            print "Mary pets the " + cage.animal + "."
        return pet_function
    
    yield (animal, partial(gotimes, scoped_cage(cage)))
    
  • Привязка переменной в качестве значения по умолчанию для параметра ключевого слова:

    def pet_function(cage=cage):
        print "Mary pets the " + cage.animal + "."
    
    yield (animal, partial(gotimes, pet_function))
    

Нет необходимости определять функцию scoped_cage в цикле, компиляция выполняется только один раз, а не на каждой итерации цикла.

Ладно, возьми меня на это, я знаю, что это будет выглядеть ужасно запутанным, но, пожалуйста, помогите мне понять, что происходит.

from functools import partial

class Cage(object):
    def __init__(self, animal):
        self.animal = animal

def gotimes(do_the_petting):
    do_the_petting()

def get_petters():
    for animal in ['cow', 'dog', 'cat']:
        cage = Cage(animal)

        def pet_function():
            print "Mary pets the " + cage.animal + "."

        yield (animal, partial(gotimes, pet_function))

funs = list(get_petters())

for name, f in funs:
    print name + ":", 
    f()

дает:

cow: Mary pets the cat.
dog: Mary pets the cat.
cat: Mary pets the cat.

Итак, в основном, почему я не получаю трех разных животных? Разве cage «упакована» в локальную область вложенной функции? Если нет, то как вызов вложенной функции ищет локальные переменные?

Я знаю, что выполнение таких проблем обычно означает, что человек «делает это неправильно», но я хотел бы понять, что происходит.


Мое понимание состоит в том, что клетка рассматривается в пространстве имен родительских функций, когда полученная функция pet_function фактически вызывается, а не раньше.

Поэтому, когда вы делаете

funs = list(get_petters())

Вы создаете 3 функции, которые найдут последнюю созданную клетку.

Если вы замените свой последний цикл:

for name, f in get_petters():
    print name + ":", 
    f()

Вы действительно получите:

cow: Mary pets the cow.
dog: Mary pets the dog.
cat: Mary pets the cat.




nested-function