Как вложенные функции работают в Python?

def maker(n):
    def action(x):
        return x ** n
    return action

f = maker(2)
print(f)
print(f(3))
print(f(4))

g = maker(3)
print(g(3))

print(f(3)) # still remembers 2

Почему вложенная функция помнит первое значение 2 даже при том, что maker() возвратился и вышел к этому времени action() назван?

56
задан Rob Bednark 31 January 2015 в 14:05
поделиться

7 ответов

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

Смотрите на это как на "динамическое создание функции".

def maker(n):
  def action(x):
    return x ** n
  return action

f = maker(2)
--> def action(x):
-->   return x ** 2

Это основное поведение питона, он делает то же самое с множественными присваиваниями.

a = 1
b = 2
a, b = b, a

Python читает это как

a, b = 2, 1

Он вставляет значения в основном перед тем, как делать что-либо с ними.

.
30
ответ дан 26 November 2019 в 17:22
поделиться

Вы в основном создаете закрытие .

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

Связанное с этим чтение: Закрытие: почему оно так полезно?

Закрытие - это просто более удобный способ дать функции доступ к локальному состоянию.

Из http://docs.python.org/reference/compound_stmts.html:

Примечание программиста: Функции - первоклассные объекты. Форма 'def', выполняемая внутри определения функции, определяет локальную функцию, которая может быть возвращена или передана. Свободные переменные, используемые во вложенной функции, могут получить доступ к локальным переменным функции, содержащей Def. Подробнее см. раздел Именование и привязка.

38
ответ дан 26 November 2019 в 17:22
поделиться

Вы определяете TWO функции. При вызове

f = maker(2)

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

f(2) --> 4
f(3) --> 6

Затем вы определяете ANOTHER DIFFERENT FUNCTION

g = maker(3)

, которая возвращает в три раза большее число

g(3) ---> 9

Но это TWO разные функции, это не одна и та же функция, на которую ссылаются, каждая из них является независимой. Даже в области видимости внутри функции 'maker' вызывается одна и та же, это не одна и та же функция, каждый раз при вызове maker() вы определяете другую функцию. Это как локальная переменная, каждый раз при вызове функции принимает одно и то же имя, но может содержать разные значения. В этом случае переменная 'action' содержит функцию (которая может быть разной)

.
14
ответ дан 26 November 2019 в 17:22
поделиться

Когда вы создаете функцию с ключевым словом def, вы делаете именно это: создаете новый объект функции и присваиваете его переменной. В коде, который вы дали, объект функции new присваивается локальной переменной, называемой action.

Когда вы вызываете его второй раз, вы создаете объект второй функции. Таким образом, f указывает на первый объект функции (квадрат-значение), а g - на второй объект функции (куб-значение). Когда Python видит "f(3)", это означает "выполнить объект функции, на который указывает переменная f, и передать ему значение 3". f и g и различные объекты функции и таким образом вернуть различные значения.

.
0
ответ дан 26 November 2019 в 17:22
поделиться

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

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

.
9
ответ дан 26 November 2019 в 17:22
поделиться

Люди правильно ответили на вопрос о закрытии, а именно: действительное значение для "n" внутри действия - это последнее значение, которое у него было при вызове "maker".

Один из простых способов преодолеть это - сделать свой freevar (n) переменной внутри функции "действия", которая получает копию "n" в момент выполнения:

Самый простой способ сделать это - установить "n" в качестве параметра, чьим значением по умолчанию является "n" в момент создания. Это значение для "n" остается фиксированным, так как параметры по умолчанию для функции хранятся в кортеже, который является атрибутом самой функции (в данном случае action.func_defaults):

def maker(n):
    def action(x, k=n):
        return x ** k
    return action

Usage:

f = maker(2) # f is action(x, k=2)
f(3)   # returns 3^2 = 9
f(3,3) # returns 3^3 = 27
1
ответ дан 26 November 2019 в 17:22
поделиться

Поскольку во время создания функции n было 2, то Ваша функция:

def action(x):
    return x ** 2

Когда Вы вызываете f(3), x устанавливается на 3, то Ваша функция будет возвращаться 3 ** 2

2
ответ дан 26 November 2019 в 17:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: