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

3zz*_*zzy 56 python closures nested function 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
Run Code Online (Sandbox Code Playgroud)

为什么嵌套函数会记住第一个值,2即使maker()已经返回并退出时action()被调用?

mik*_*iku 38

你基本上是在创建一个闭包.

在计算机科学中,闭包是一个具有自由变量的第一类函数,它们在词法环境中受到约束.据说这种功能是"封闭"其自由变量.

相关阅读:闭包:为什么它们如此有用?

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

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

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


Tor*_*amo 30

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

将其视为"动态创建功能".

def maker(n):
  def action(x):
    return x ** n
  return action

f = maker(2)
--> def action(x):
-->   return x ** 2
Run Code Online (Sandbox Code Playgroud)

这是python中的基本行为,它对多个赋值也是如此.

a = 1
b = 2
a, b = b, a
Run Code Online (Sandbox Code Playgroud)

Python将其读作

a, b = 2, 1
Run Code Online (Sandbox Code Playgroud)

它基本上在使用它们之前插入值.


Khe*_*ben 14

您正在定义两个功能.你打电话的时候

f = maker(2)
Run Code Online (Sandbox Code Playgroud)

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

f(2) --> 4
f(3) --> 6
Run Code Online (Sandbox Code Playgroud)

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

g = maker(3)
Run Code Online (Sandbox Code Playgroud)

返回三倍的数字

g(3) ---> 9
Run Code Online (Sandbox Code Playgroud)

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


Luk*_*man 9

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

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


suj*_*ari 6

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

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
Run Code Online (Sandbox Code Playgroud)

现在,让我们尝试调用此函数。

>>> another = print_msg("Hello")
>>> another()
Hello
Run Code Online (Sandbox Code Playgroud)

这很不寻常。print_msg()使用字符串调用该函数"Hello",并将返回的函数绑定到该名称another。在调用时another(),尽管我们已经完成了该print_msg()函数的执行,但仍然记得该消息。这种将一些数据("Hello")附加到代码上的技术在Python中称为闭包。

何时使用瓶盖?

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

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)
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参考此博客。