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()
?
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()
?
您基本上是在创建一个闭包。
在计算机科学中,闭包是一个一流的函数,具有绑定在词法环境中的自由变量。这样的函数被称为“封闭”了它的自由变量。
相关阅读:闭包:为什么它们如此有用?
闭包只是让函数访问本地状态的一种更方便的方法。
从http://docs.python.org/reference/compound_stmts.html:
程序员注:函数是一等对象。在函数定义中执行的“def”形式定义了一个可以返回或传递的本地函数。嵌套函数中使用的自由变量可以访问包含 def 的函数的局部变量。有关详细信息,请参阅命名和绑定部分。
您可以将其视为源自父函数的所有变量都被子函数中的实际值替换。这样,就不需要跟踪父函数的范围来使子函数正确运行。
将其视为“动态创建函数”。
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
它基本上在对它们进行任何操作之前插入这些值。
您正在定义两个函数。你打电话时
f = maker(2)
您正在定义一个返回两倍数字的函数,所以
f(2) --> 4
f(3) --> 6
然后,您定义另一个不同的功能
g = maker(3)
返回三倍的数字
g(3) ---> 9
但它们是两个不同的函数,引用的函数不是同一个函数,每一个都是独立的。即使在函数'maker'的范围内调用相同,也不是相同的函数,每次调用maker()
时都定义了不同的函数。它就像一个局部变量,每次调用该函数时都取相同的名称,但可以包含不同的值。在这种情况下,变量“action”包含一个函数(可以不同)
让我们看一下编写内部函数的三个常见原因。
即使变量超出范围或函数本身已从当前命名空间中删除,封闭范围内的值也会被记住。
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"
并且返回的函数绑定到 name another
。在调用another()
时,尽管我们已经完成了print_msg()
函数的执行,但仍然记得该消息。这种将一些数据 ( "Hello"
) 附加到代码的技术在 Python 中称为闭包。
那么闭包有什么用呢?闭包可以避免使用全局值并提供某种形式的数据隐藏。它还可以为该问题提供面向对象的解决方案。当一个类中要实现的方法很少(大多数情况下是一个方法)时,闭包可以提供另一种更优雅的解决方案。参考
封装的一般概念是隐藏和保护内部世界免受外部世界的影响,这里内部函数只能在外部世界内部访问,并且不受函数外部发生的任何事情的影响。
也许您有一个巨大的函数,它在许多地方执行相同的代码块。例如,您可能编写一个处理文件的函数,并且您希望接受打开的文件对象或文件名:
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)
有关更多信息,您可以参考此博客。
因为在您创建函数时n
was 2
,所以您的函数是:
def action(x):
return x ** 2
当您调用 f(3) 时,x
设置为3
,因此您的函数将返回3 ** 2
人们对闭包的回答是正确的,即:动作内部“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
一种用途是返回一个维护参数的函数。
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 关键字创建函数时,您就是在这样做:您正在创建一个新的函数对象并将其分配给一个变量。在您提供的代码中,您将该新函数对象分配给一个名为 action 的局部变量。
当您第二次调用它时,您正在创建第二个函数对象。所以 f 指向第一个函数对象(平方值),g 指向第二个函数对象(立方值)。当 Python 看到“f(3)”时,它的意思是“执行指向变量 f 的函数对象并将值 3 传递给它”。f 和 g 以及不同的函数对象,因此返回不同的值。