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()
时返回并退出?
答案 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)
让我们看看编写内部函数的三个常见原因。
即使变量超出范围或从当前命名空间中删除了函数本身,也会记住封闭范围中的值。
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
封装的一般概念是隐藏和保护内部世界免于外部世界,这里内部函数只能在外部函数内部访问,并且不受函数外部发生的任何事件的影响。
也许你有一个巨大的功能,可以在很多地方执行相同的代码块。例如,您可能编写一个处理文件的函数,并且您希望接受打开的文件对象或文件名:
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)
因为在您创建该功能时,n
为2
,所以您的功能是:
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以及不同的函数对象因此返回不同的值。