Как вложенные функции работают в 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()?

Ответ 1

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

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

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

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

Это базовое поведение в python, оно делает то же самое с несколькими присваиваниями.

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

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

a, b = 2, 1

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

Ответ 2

В основном вы создаете closure.

В информатике закрытие является функцией первого класса со свободными переменными, которые связаны в лексической среде. Такая функция называется "замкнутой" над ее свободными переменными.

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

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

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

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

Ответ 3

Вы определяете ДВЕ функции. Когда вы вызываете

f = maker(2)

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

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

Затем вы определяете ДРУГИЕ РАЗНЫЕ ФУНКЦИИ

g = maker(3)

которые возвращают три раза число

g(3) ---> 9

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

Ответ 4

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

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

Ответ 5

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

def action(x):
    return x ** 2

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

Ответ 6

Давайте рассмотрим три общие причины написания внутренних функций.

1. Замыкания и Factory Функции

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

def print_msg(msg):
    """This is the outer enclosing function"""

    def printer():
        """This is the nested function"""
        print(msg)

    return printer  # this got changed

Теперь попробуйте вызвать эту функцию.

>>> another = print_msg("Hello")
>>> another()
Hello

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

Когда использовать закрытие?

Итак, для чего нужны замыкания? Закрытие может избежать использования глобальных значений и обеспечивает скрытую скрытую информацию. Он также может обеспечить объектно-ориентированное решение проблемы. Когда в классе реализовано несколько методов (один метод в большинстве случаев), замыкания могут обеспечить альтернативные и более элегантные решения. Ссылка

2. Инкапсуляция:

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

3. Keepin it DRY

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

def process(file_name):
    def do_stuff(file_process):
        for line in file_process:
            print(line)
    if isinstance(file_name, str):
        with open(file_name, 'r') as f:
            do_stuff(f)
    else:
        do_stuff(file_name)

Для получения дополнительной информации вы можете отсылать этот блог.

Ответ 7

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

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

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

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

Использование:

f = maker(2) # f is action(x, k=2)
f(3)   # returns 3^2 = 9
f(3,3) # returns 3^3 = 27

Ответ 8

Одно использование - вернуть функцию, поддерживающую параметр.

def outer_closure(a):
    #  parm = a               <- saving a here isn't needed
    def inner_closure():
        #return parm
        return a              # <- a is remembered 
    return inner_closure

# set parm to 5 and return address of inner_closure function
x5 = outer_closure(5)
x5()
>5

x6 = outer_closure(6)
x6()
>6

# x5 inner closure function instance of parm persists 
x5()
>5

Ответ 9

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

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