嵌套函数如何在Python中运行?

时间:2010-01-05 12:29:21

标签: python function nested closures nested-function

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()时返回并退出?

9 个答案:

答案 0 :(得分:38)

您基本上是在创建closure

  

在计算机科学中,闭包是一个具有自由变量的第一类函数,它们在词法环境中受到约束。据说这样的函数是“关闭”它的自由变量。

相关阅读:Closures: why are they so useful?

  

闭包只是一种让函数访问本地状态的更方便的方法。

来自http://docs.python.org/reference/compound_stmts.html

  

程序员注意:函数是一流的对象。在函数定义中执行的'def'形式定义了可以返回或传递的本地函数。嵌套函数中使用的自由变量可以访问包含def的函数的局部变量。有关详细信息,请参阅命名和绑定部分。

答案 1 :(得分:30)

您可以看到它,因为父函数中的所有变量都被子函数中的实际值替换。这样,就不需要跟踪父函数的范围,以使子函数正确运行。

将其视为“动态创建功能”。

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 :(得分:14)

您正在定义两个功能。当你打电话

f = maker(2)

你定义的函数返回两倍的数字,所以

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

然后,您定义另一个不同的功能

g = maker(3)

返回数字的三倍

g(3) ---> 9

但它们是两个不同的功能,它不是引用的相同功能,每个功能都是独立的功能。即使在函数'maker'内部的范围被称为相同,也不是相同的函数,每次调用maker()时你定义的是不同的函数。它就像一个局部变量,每次调用该函数时都使用相同的名称,但可以包含不同的值。   在这种情况下,变量'action'包含一个函数(可以是不同的)

答案 3 :(得分:9)

这就是所谓的“closure”。简单地说,对于大多数(如果不是所有的)将函数视为first-class object的编程语言,只要函数仍然存活,函数对象中使用的任何变量都被包含(即记住)。如果您知道如何使用它,这是一个强大的概念。

在您的示例中,嵌套的action函数使用变量n,因此它围绕该变量形成一个闭包,并记住它以供以后的函数调用。

答案 4 :(得分:3)

让我们看看编写内部函数的三个常见原因。

1。闭包和工厂功能

即使变量超出范围或从当前命名空间中删除了函数本身,也会记住封闭范围中的值。

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中称为闭包。

何时使用闭包?

那么封闭有什么用呢?闭包可以避免使用全局值并提供某种形式的数据隐藏。它还可以为问题提供面向对象的解决方案。当在类中实现的方法很少(大多数情况下是一种方法)时,闭包可以提供替代和更优雅的解决方案。 Reference

2。封装:

封装的一般概念是隐藏和保护内部世界免于外部世界,这里内部函数只能在外部函数内部访问,并且不受函数外部发生的任何事件的影响。

3。保持'干'

也许你有一个巨大的功能,可以在很多地方执行相同的代码块。例如,您可能编写一个处理文件的函数,并且您希望接受打开的文件对象或文件名:

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)

有关详情,请参阅this博客。

答案 5 :(得分:2)

因为在您创建该功能时,n2,所以您的功能是:

def action(x):
    return x ** 2

当您拨打f(3)时,x设置为3,因此您的函数将返回3 ** 2

答案 6 :(得分:1)

人们正确地回答了关闭,即:“n”内部动作的有效值是每当调用“maker”时它的最后一个值。

解决这个问题的一种简单方法是让你的freevar(n)成为“action”函数中的一个变量,它在运行时会收到一个“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

答案 7 :(得分:1)

一种用途是返回维护参数的函数。

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

答案 8 :(得分:0)

使用def关键字创建函数时,您正是这样做的:您正在创建一个新的函数对象并将其分配给变量。在您给出的代码中,您将新的函数对象分配给名为action的局部变量。

当您第二次调用它时,您正在创建第二个函数对象。因此f指向第一个函数对象(square-the-value),g指向第二个函数对象(cube-the-value)。当Python看到“f(3)”时,它意味着“执行指向变量f的函数对象并将其传递给值3”。 f和g以及不同的函数对象因此返回不同的值。