嵌套函数如何在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函数的时候, n
是2
,所以你的函数是:
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和不同的函数对象,因此返回不同的值。
- 函数返回一个lambdaexpression式
- 将一个额外的parameter passing给一个callback函数
- asynchronousJavaScript执行如何发生? 什么时候不用return语句?
- 函数名称在C ++中:是否大写?
- 为什么`obj.foo = function(){};`不给函数赋“foo”这个名字?
- Javascript函数来获得两个数字之间的差异
- 为什么一个C数组在传递给一个函数时有一个错误的sizeof()值?
- 如何在.NET中传递string?
- 为什么在JavaScript中,“Object instanceof Function”和“Function instanceof Object”都返回true?