嵌套函数如何在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()的时候退出?

你可以看到,所有在父函数中的variables被它们在子函数中的实际值所替代。 这样,就不需要跟踪父函数的作用域来使子函数正确运行。

把它看作是“dynamic创build一个函数”。

 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 

它基本上插入值之前做任何事情。

你基本上正在创build一个闭包 。

在计算机科学中,闭包是在词汇环境中绑定自由variables的一类函数。 这样的function被称为“closures”其自由variables。

相关阅读: closures:为什么他们如此有用?

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

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

程序员的注意:函数是一stream的对象。 在函数定义中执行的“def”forms定义了一个可以返回或传递的本地函数。 嵌套函数中使用的自由variables可以访问包含def的函数的局部variables。 有关详细信息,请参见命名和绑定一节。

你正在定义两个函数。 你打电话时

 f = maker(2) 

你正在定义一个返回两倍数的函数,所以

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

然后,你定义另一个不同的function

 g = maker(3) 

返回三倍的数字

 g(3) ---> 9 

但是它们是两个不同的函数,它们不是相同的函数,每个函数都是独立的函数。 即使在函数“制造商”的范围内被称为相同,不是相同的function,每次你打电话maker()你正在定义一个不同的function。 它就像一个局部variables,每次调用该函数时都会使用相同的名称,但可以包含不同的值。 在这种情况下,variables'action'包含一个函数(可以是不同的)

这就是所谓的“ 封闭 ”。 简单地说,对于大多数(如果不是所有)将函数视为第一类对象的编程语言,函数对象中使用的任何variables都被封闭(即记住),只要该函数仍然存在。 如果你知道如何使用它,这是一个强大的概念。

在你的例子中,嵌套的action函数使用variablesn所以它围绕该variables形成一个闭包,并记住它以便以后的函数调用。

因为当你创build函数的时候, n2 ,所以你的函数是:

 def action(x): return x ** 2 

当你调用f(3)时, x被设置为3 ,所以你的函数将返回3 ** 2

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

1.闭包和工厂function

即使variables超出作用域,或者函数本身从当前名称空间中移除,封闭作用域中的值也会被记住。

 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 

这是不寻常的。 用string"Hello"调用print_msg()函数,返回的函数被绑定到another名字上。 在调用another() ,虽然我们已经完成了print_msg()函数的执行,但仍然记得这个消息。 一些数据( "Hello" )被附加到代码中的技术在Python中称为closure。

何时使用闭包?

那么闭合有什么好处呢? closures可以避免使用全局值,并提供某种forms的数据隐藏。 它也可以为这个问题提供一个面向对象的解决scheme。 当一个课程中只有很less的方法(大多数情况下是一种方法)时,闭包可以提供一个备用和更优雅的解决scheme。 参考

2.封装:

封装的一般概念是隐藏和保护内部世界不受外部影响,这里内部function只能在外部访问,而不受外部function影响。

保持干爽

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

 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) 

更多你可以参考这个博客。

人们对于closures的回答是正确的,那就是:行动中“n”的有效值是“maker”被调用时的最后一个值。

一个简单的方法来解决这个问题,就是让你的freevar(n)在“action”函数里面有一个variables,它在运行的时候会收到一个“n”

最简单的方法是将“n”设置为在创build时默认值为“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 

一个用途是返回一个保存参数的函数。

 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 

当你用def关键字创build一个函数时,你完全是这样做的:你正在创build一个新的函数对象并将其分配给一个variables。 在你给你的代码中,把这个新的函数对象分配给一个名为action的局部variables。

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