为什么python嵌套函数不称为闭包?

为什么python嵌套函数不称为闭包?,python,closures,nested-function,Python,Closures,Nested Function,我在Python中见过并使用过嵌套函数,它们与闭包的定义相匹配。那么为什么它们被称为嵌套函数而不是闭包呢 嵌套函数不是闭包,是因为外部世界不使用它们吗 更新:我在读关于闭包的书,它让我想到了关于Python的这个概念。我在下面的一篇评论中搜索并找到了某人提到的文章,但我无法完全理解该文章中的解释,因此我提出了这个问题。当函数从已完成执行的封闭范围访问局部变量时,就会发生闭包 def make_printer(msg): def printer(): print msg

我在Python中见过并使用过嵌套函数,它们与闭包的定义相匹配。那么为什么它们被称为嵌套函数而不是闭包呢

嵌套函数不是闭包,是因为外部世界不使用它们吗


更新:我在读关于闭包的书,它让我想到了关于Python的这个概念。我在下面的一篇评论中搜索并找到了某人提到的文章,但我无法完全理解该文章中的解释,因此我提出了这个问题。

当函数从已完成执行的封闭范围访问局部变量时,就会发生闭包

def make_printer(msg):
    def printer():
        print msg
    return printer

printer = make_printer('Foo!')
printer()
调用make_printer时,将在堆栈上放置一个新帧,打印机函数的编译代码作为常量,msg的值作为本地值。然后它创建并返回函数。因为函数printer引用了msg变量,所以make_printer函数返回后,它将保持活动状态

因此,如果嵌套函数没有

访问封闭作用域的局部变量, 当在该范围外执行时,请执行该操作, 那么它们就不是闭包了

>>> class f2:
...     def __init__(self):
...         self.a = 0
...     def __call__(self, arg):
...         self.a += arg
...         return(self.a)
...
>>> f=f2()
>>> f(2)
2
>>> f(2)
4
>>> f(4)
8
>>> f(8)
16

# **OR**
>>> f=f2() # **re-initialize**
>>> f(f(f(f(2)))) # **nested**
16

# handy in list comprehensions to accumulate values
>>> [f(i) for f in [f2()] for i in [2,2,4,8]][-1] 
16
下面是一个嵌套函数的示例,它不是闭包

def make_printer(msg):
    def printer(msg=msg):
        print msg
    return printer

printer = make_printer("Foo!")
printer()  #Output: Foo!
这里,我们将该值绑定到参数的默认值。当创建函数printer时会发生这种情况,因此在make_printer返回后,不需要维护对打印机外部msg值的引用。在此上下文中,msg只是函数printer的一个普通局部变量

这个问题已经有人回答了

然而,有些人可能对变量如何存储在引擎盖下感兴趣

在进入代码段之前:

闭包是从封闭环境继承变量的函数。当您将一个函数回调作为参数传递给另一个将执行I/O的函数时,该回调函数将在稍后被调用,并且该函数将几乎神奇地记住声明它的上下文,以及该上下文中可用的所有变量

如果函数不使用自由变量,它就不会形成闭包

如果有另一个内部级别使用自由变量,那么前面的所有级别都会在末尾保存词法环境示例

python<3.X中的函数属性func_closure或python>3.X中的函数属性func_closure保存自由变量

python中的每个函数都有这个闭包属性,但是如果没有自由变量,它不会保存任何内容

示例:包含闭包属性,但内部没有内容,因为没有自由变量

def counter_in(initial_value=0):
    # initial_value is the free variable
    def inc(increment=1):
        nonlocal initial_value
        initial_value += increment
        return print(initial_value)
    return inc
注意:必须创建一个闭包

我将使用与上面相同的片段进行解释:

>>> def make_printer(msg):
...     def printer():
...         print msg
...     return printer
...
>>> printer = make_printer('Foo!')
>>> printer()  #Output: Foo!
所有Python函数都有一个closure属性,所以让我们检查与closure函数相关联的封闭变量

  myClosingOverFunc=counter_in(2)
下面是函数打印机的属性func_闭包

在上面的输出中,您可以看到单元格内容,让我们看看它存储了什么:

>>> printer.func_closure[0].cell_contents
'Foo!'    
>>> type(printer.func_closure[0].cell_contents)
<type 'str'>
>>>
在上面的代码段中,我没有在printer函数中打印msg,因此它不会创建任何自由变量。因为没有自由变量,所以闭包中没有内容。这正是我们上面看到的

现在,我将解释另一个不同的片段,用闭包清除所有自由变量:

在这里,当我们调用inn时,它将引用所有save-free变量,因此我们得到I-am-free变量

给出:

In [17]: my_func=nested1(8)
nested1 has 8

In [21]: my_func(5)
nested2 has 5 and it can reach to 8
Out[21]: 13
这是一个什么是闭包以及如何使用闭包的示例。

Python对闭包的支持很弱。要了解我的意思,请以下面使用JavaScript闭包的计数器为例:

function initCounter(){
    var x = 0;
    function counter  () {
        x += 1;
        console.log(x);
    };
    return counter;
}

count = initCounter();

count(); //Prints 1
count(); //Prints 2
count(); //Prints 3
闭包非常优雅,因为它使这样编写的函数能够拥有内部内存。从Python2.7开始,这是不可能的。如果你尝试

def initCounter():
    x = 0;
    def counter ():
        x += 1 ##Error, x not defined
        print x
    return counter

count = initCounter();

count(); ##Error
count();
count();

您将得到一个错误,说明x未定义。但如果别人告诉你你可以打印,那怎么可能呢?这是因为Python是如何管理函数变量范围的。虽然内部函数可以读取外部函数的变量,但不能写入它们

真是太遗憾了。但是使用只读闭包,您至少可以实现Python提供的语法糖

更新

正如前面所指出的,有一些方法可以解决python的范围限制,我将公开一些方法

一,。一般不建议使用全局关键字

二,。在Python3.x中,使用@unutbu和@leewz建议的关键字

三,。定义一个简单的可修改类对象

并在initCounter中创建一个对象范围来存储变量

def initCounter ():
    scope = Object()
    scope.x = 0
    def counter():
        scope.x += 1
        print scope.x

    return counter
由于作用域实际上只是一个引用,使用其字段执行的操作不会真正修改作用域本身,因此不会出现错误

四,。正如@unutbu指出的,另一种方法是将每个变量定义为一个数组x=[0],并修改它的第一个元素x[0]+=1。银 因为x本身没有被修改,所以没有出现错误

五,。正如@raxacoricocfallapatorius所建议的,您可以将x作为计数器的属性


我遇到了一种情况,需要一个单独但持久的名称空间。 我上过课。我不知道其他的。 隔离但持久的名称是闭包

>>> class f2:
...     def __init__(self):
...         self.a = 0
...     def __call__(self, arg):
...         self.a += arg
...         return(self.a)
...
>>> f=f2()
>>> f(2)
2
>>> f(2)
4
>>> f(4)
8
>>> f(8)
16

# **OR**
>>> f=f2() # **re-initialize**
>>> f(f(f(f(2)))) # **nested**
16

# handy in list comprehensions to accumulate values
>>> [f(i) for f in [f2()] for i in [2,2,4,8]][-1] 
16

Python2没有闭包——它有类似闭包的变通方法

>>> class f2:
...     def __init__(self):
...         self.a = 0
...     def __call__(self, arg):
...         self.a += arg
...         return(self.a)
...
>>> f=f2()
>>> f(2)
2
>>> f(2)
4
>>> f(4)
8
>>> f(8)
16

# **OR**
>>> f=f2() # **re-initialize**
>>> f(f(f(f(2)))) # **nested**
16

# handy in list comprehensions to accumulate values
>>> [f(i) for f in [f2()] for i in [2,2,4,8]][-1] 
16
在已经给出的答案中有很多例子——将变量复制到内部函数,修改内部函数上的对象,等等

在Python 3中,支持更加明确和简洁:

def closure():
    count = 0
    def inner():
        nonlocal count
        count += 1
        print(count)
    return inner
用法:

start = closure()
another = closure() # another instance, with a different stack

start() # prints 1
start() # prints 2

another() # print 1

start() # prints 3

nonlocal关键字将内部函数绑定到显式提到的外部变量,实际上将其包围。因此更明确地说是一个“闭包”。

如果这有助于让事情更清楚,我想提供python和JS示例之间的另一个简单比较

JS:

并执行:

a = make(); g = a[0]; s = a[1];
s(2); g(); // 2
s(3); g(); // 3
Python:

def make (): 
  cl = 1
  def gett ():
    print(cl);
  def sett (val):
    cl = val
  return gett, sett
并执行:

g, s = make()
g() #1
s(2); g() #1
s(3); g() #1

原因:正如上面许多其他人所说,在python中,如果内部作用域中有一个赋值给同名变量,则会在内部作用域中创建一个新引用。JS不是这样,除非你用var关键字显式地声明一个。

人们对闭包是什么感到困惑。闭包不是内部功能。关闭的含义是关闭的行为。所以内部函数在一个称为自由变量的非局部变量上闭合

def counter_in(initial_value=0):
    # initial_value is the free variable
    def inc(increment=1):
        nonlocal initial_value
        initial_value += increment
        return print(initial_value)
    return inc
当您在中调用计数器_时,将返回包含自由变量初始_值的inc函数。所以我们创建了一个闭包。人们称inc为闭包函数,我认为这让人困惑,人们认为ok的内部函数是闭包。事实上,inc并不是一个闭包,因为它是闭包的一部分,为了让生活变得简单,他们称之为闭包函数

  myClosingOverFunc=counter_in(2)
这将返回inc函数,该函数在自由变量初始值上关闭。调用myClosingOverFunc时

它将打印2

当python看到一个闭包系统存在时,它会创建一个名为CELL的新obj。这将只存储自由变量的名称,在本例中,它是初始值。此单元格obj将指向另一个存储初始_值的对象

在我们的示例中,外部函数和内部函数中的初始_值将指向此单元格对象,而此单元格对象将指向初始_值的值

所以,当你在它的范围内调用counter_时,它就消失了,但这并不重要。因为变量初始值直接引用单元格Obj。它间接引用了初始值的值。这就是为什么即使外部函数的作用域消失了,内部函数仍然可以访问自由变量

假设我想写一个函数,它接受一个函数作为参数,并返回这个函数被调用的次数

def counter(fn):
    # since cnt is a free var, python will create a cell and this cell will point to the value of cnt
    # every time cnt changes, cell will be pointing to the new value
    cnt = 0

    def inner(*args, **kwargs):
        # we cannot modidy cnt with out nonlocal
        nonlocal cnt
        cnt += 1
        print(f'{fn.__name__} has been called {cnt} times')
        # we are calling fn indirectly via the closue inner
        return fn(*args, **kwargs)
    return inner
      
在这个例子中,cnt是我们的自由变量,内部+cnt创建闭包。当python看到这一点时,它将创建一个单元格Obj,cnt将始终直接引用该单元格Obj,而该单元格将引用内存中存储cnt值的另一个Obj。最初cnt=0

 cnt   ======>>>>  CELL  =============>  0
当您通过传递参数countermyFunc调用内部函数时,这将使cnt增加1。因此,我们的引用模式将更改如下:

 cnt   ======>>>>  CELL  =============>  1  #first counter(myFunc)()
 cnt   ======>>>>  CELL  =============>  2  #second counter(myFunc)()
 cnt   ======>>>>  CELL  =============>  3  #third counter(myFunc)()
这只是闭包的一个实例。通过传递另一个函数,可以创建多个闭包实例

counter(differentFunc)()
这将创建与上述不同的单元格obj。我们刚刚创建了另一个闭包实例

 cnt  ======>>  difCELL  ========>  1  #first counter(differentFunc)()
 cnt  ======>>  difCELL  ========>  2  #secon counter(differentFunc)()
 cnt  ======>>  difCELL  ========>  3  #third counter(differentFunc)()


  

有趣的是,有人在谷歌上找到了我这个日期是2006年12月的:。我不确定外部复制品是不是不受欢迎?更多信息。你的答案比我的好得多,你的观点很好,但是如果我们要使用最严格的函数编程定义,你的例子是函数吗?已经有一段时间了,我不记得严格的函数编程是否允许不返回值的函数。如果你认为返回值是没有的,但是这是另一个主题。MikoBi,我不确定我们是否需要考虑函数编程,因为Python不是真正的函数语言,尽管它确实可以被使用。但是,不,内在功能不是那种意义上的功能,因为它们的全部目的是产生副作用。创建一个函数很容易就能很好地说明这些要点,@mikerobi:一个代码块是否是一个闭包取决于它是否在其环境中关闭,而不是你所称的闭包。它可以是一个例程、函数、过程、方法、块、子例程等等。在Ruby中,方法不能是闭包,只能是块。在Java中,方法不能是闭包,但类可以。这并没有使他们的结局减少。虽然它们只关闭了一些变量,并且不能修改它们,这使得它们几乎毫无用处。你可以说一个方法只是一个封闭在自我之上的过程。在JavaScript/Python中,这几乎是正确的。@J?
def counter_in(initial_value=0):
    # initial_value is the free variable
    def inc(increment=1):
        nonlocal initial_value
        initial_value += increment
        return print(initial_value)
    return inc
  myClosingOverFunc=counter_in(2)
 myClosingOverFunc() 
  variable initial_value =====>> CELL ==========>> value of initial_value
def counter(fn):
    # since cnt is a free var, python will create a cell and this cell will point to the value of cnt
    # every time cnt changes, cell will be pointing to the new value
    cnt = 0

    def inner(*args, **kwargs):
        # we cannot modidy cnt with out nonlocal
        nonlocal cnt
        cnt += 1
        print(f'{fn.__name__} has been called {cnt} times')
        # we are calling fn indirectly via the closue inner
        return fn(*args, **kwargs)
    return inner
      
 cnt   ======>>>>  CELL  =============>  0
 cnt   ======>>>>  CELL  =============>  1  #first counter(myFunc)()
 cnt   ======>>>>  CELL  =============>  2  #second counter(myFunc)()
 cnt   ======>>>>  CELL  =============>  3  #third counter(myFunc)()
counter(differentFunc)()
 cnt  ======>>  difCELL  ========>  1  #first counter(differentFunc)()
 cnt  ======>>  difCELL  ========>  2  #secon counter(differentFunc)()
 cnt  ======>>  difCELL  ========>  3  #third counter(differentFunc)()