标签归档:nested-function

嵌套函数中的局部变量

问题:嵌套函数中的局部变量

好的,请耐心等待,我知道它看起来会令人费解,但是请帮助我了解发生了什么。

from functools import partial

class Cage(object):
    def __init__(self, animal):
        self.animal = animal

def gotimes(do_the_petting):
    do_the_petting()

def get_petters():
    for animal in ['cow', 'dog', 'cat']:
        cage = Cage(animal)

        def pet_function():
            print "Mary pets the " + cage.animal + "."

        yield (animal, partial(gotimes, pet_function))

funs = list(get_petters())

for name, f in funs:
    print name + ":", 
    f()

给出:

cow: Mary pets the cat.
dog: Mary pets the cat.
cat: Mary pets the cat.

所以基本上,为什么我没有得到三种不同的动物?难道不是cage“打包”到嵌套函数的局部作用域中吗?如果不是,对嵌套函数的调用如何查找局部变量?

我知道遇到这些问题通常意味着一个人“做错了”,但是我想了解会发生什么。

Okay, bear with me on this, I know it’s going to look horribly convoluted, but please help me understand what’s happening.

from functools import partial

class Cage(object):
    def __init__(self, animal):
        self.animal = animal

def gotimes(do_the_petting):
    do_the_petting()

def get_petters():
    for animal in ['cow', 'dog', 'cat']:
        cage = Cage(animal)

        def pet_function():
            print "Mary pets the " + cage.animal + "."

        yield (animal, partial(gotimes, pet_function))

funs = list(get_petters())

for name, f in funs:
    print name + ":", 
    f()

Gives:

cow: Mary pets the cat.
dog: Mary pets the cat.
cat: Mary pets the cat.

So basically, why am I not getting three different animals? Isn’t the cage ‘packaged’ into the local scope of the nested function? If not, how does a call to the nested function look up the local variables?

I know that running into these kind of problems usually means one is ‘doing it wrong’, but I’d like to understand what happens.


回答 0

嵌套函数在执行时(而不是在定义时)从父范围中查找变量。

编译函数主体,然后验证“自由”变量(未在函数本身中通过赋值定义),然后将其作为闭包单元绑定到函数,并且代码使用索引引用每个单元格。pet_function因此具有一个自由变量(cage),然后将其通过一个闭合单元引用,索引为0的闭合本身指向局部变量cageget_petters功能。

当您实际调用该函数时,该闭包将用于在您调用该函数时查看cage周围作用域中的值。问题就在这里。在您调用函数时,该函数已经完成了对其结果的计算。将在在执行过程中的一些点局部变量分配各的,和字符串,但在功能的结束,包含了最后一个值。因此,当您调用每个动态返回的函数时,就会得到打印的值。get_petterscage'cow''dog''cat'cage'cat''cat'

解决方法是不依赖闭包。您可以改用部分函数,创建新的函数作用域或将变量绑定为关键字parameter默认值

  • 部分函数示例,使用functools.partial()

    from functools import partial
    
    def pet_function(cage=None):
        print "Mary pets the " + cage.animal + "."
    
    yield (animal, partial(gotimes, partial(pet_function, cage=cage)))
  • 创建一个新的范围示例:

    def scoped_cage(cage=None):
        def pet_function():
            print "Mary pets the " + cage.animal + "."
        return pet_function
    
    yield (animal, partial(gotimes, scoped_cage(cage)))
  • 将变量绑定为关键字参数的默认值:

    def pet_function(cage=cage):
        print "Mary pets the " + cage.animal + "."
    
    yield (animal, partial(gotimes, pet_function))

无需scoped_cage在循环中定义函数,编译仅进行一次,而不是在循环的每次迭代中进行。

The nested function looks up variables from the parent scope when executed, not when defined.

The function body is compiled, and the ‘free’ variables (not defined in the function itself by assignment), are verified, then bound as closure cells to the function, with the code using an index to reference each cell. pet_function thus has one free variable (cage) which is then referenced via a closure cell, index 0. The closure itself points to the local variable cage in the get_petters function.

When you actually call the function, that closure is then used to look at the value of cage in the surrounding scope at the time you call the function. Here lies the problem. By the time you call your functions, the get_petters function is already done computing it’s results. The cage local variable at some point during that execution was assigned each of the 'cow', 'dog', and 'cat' strings, but at the end of the function, cage contains that last value 'cat'. Thus, when you call each of the dynamically returned functions, you get the value 'cat' printed.

The work-around is to not rely on closures. You can use a partial function instead, create a new function scope, or bind the variable as a default value for a keyword parameter.

  • Partial function example, using functools.partial():

    from functools import partial
    
    def pet_function(cage=None):
        print "Mary pets the " + cage.animal + "."
    
    yield (animal, partial(gotimes, partial(pet_function, cage=cage)))
    
  • Creating a new scope example:

    def scoped_cage(cage=None):
        def pet_function():
            print "Mary pets the " + cage.animal + "."
        return pet_function
    
    yield (animal, partial(gotimes, scoped_cage(cage)))
    
  • Binding the variable as a default value for a keyword parameter:

    def pet_function(cage=cage):
        print "Mary pets the " + cage.animal + "."
    
    yield (animal, partial(gotimes, pet_function))
    

There is no need to define the scoped_cage function in the loop, compilation only takes place once, not on each iteration of the loop.


回答 1

我的理解是,在实际调用产生的pet_function时而不是之前,在父函数命名空间中查找了笼子。

所以当你这样做

funs = list(get_petters())

您生成3个函数,这些函数将找到最后创建的笼子。

如果您将最后一个循环替换为:

for name, f in get_petters():
    print name + ":", 
    f()

您实际上会得到:

cow: Mary pets the cow.
dog: Mary pets the dog.
cat: Mary pets the cat.

My understanding is that cage is looked for in the parent function namespace when the yielded pet_function is actually called, not before.

So when you do

funs = list(get_petters())

You generate 3 functions which will find the lastly created cage.

If you replace your last loop with :

for name, f in get_petters():
    print name + ":", 
    f()

You will actually get :

cow: Mary pets the cow.
dog: Mary pets the dog.
cat: Mary pets the cat.

回答 2

这源于以下

for i in range(2): 
    pass

print(i)  # prints 1

迭代后,将的值i延迟存储为最终值。

作为生成器,该函数可以工作(即依次打印每个值),但是在转换为列表时,它将在生成器上运行,因此对cagecage.animal)的所有调用都返回cats。

This stems from the following

for i in range(2): 
    pass

print(i)  # prints 1

after iterating the value of i is lazily stored as its final value.

As a generator the function would work (i.e. printing each value in turn), but when transforming to a list it runs over the generator, hence all calls to cage (cage.animal) return cats.


回答 3

让我们简化问题。定义:

def get_petters():
    for animal in ['cow', 'dog', 'cat']:
        def pet_function():
            return "Mary pets the " + animal + "."

        yield (animal, pet_function)

然后,就像在问题中一样,我们得到:

>>> for name, f in list(get_petters()):
...     print(name + ":", f())

cow: Mary pets the cat.
dog: Mary pets the cat.
cat: Mary pets the cat.

但是,如果我们避免创建list()第一个:

>>> for name, f in get_petters():
...     print(name + ":", f())

cow: Mary pets the cow.
dog: Mary pets the dog.
cat: Mary pets the cat.

这是怎么回事?为什么这种微妙的差异会完全改变我们的结果?


如果我们看一下list(get_petters()),从不断变化的内存地址可以明显看出,我们确实产生了三种不同的功能:

>>> list(get_petters())

[('cow', <function get_petters.<locals>.pet_function at 0x7ff2b988d790>),
 ('dog', <function get_petters.<locals>.pet_function at 0x7ff2c18f51f0>),
 ('cat', <function get_petters.<locals>.pet_function at 0x7ff2c14a9f70>)]

但是,请看一下cell这些函数绑定到的:

>>> for _, f in list(get_petters()):
...     print(f(), f.__closure__)

Mary pets the cat. (<cell at 0x7ff2c112a9d0: str object at 0x7ff2c3f437f0>,)
Mary pets the cat. (<cell at 0x7ff2c112a9d0: str object at 0x7ff2c3f437f0>,)
Mary pets the cat. (<cell at 0x7ff2c112a9d0: str object at 0x7ff2c3f437f0>,)

>>> for _, f in get_petters():
...     print(f(), f.__closure__)

Mary pets the cow. (<cell at 0x7ff2b86b5d00: str object at 0x7ff2c1a95670>,)
Mary pets the dog. (<cell at 0x7ff2b86b5d00: str object at 0x7ff2c1a952f0>,)
Mary pets the cat. (<cell at 0x7ff2b86b5d00: str object at 0x7ff2c3f437f0>,)

对于这两个循环,cell对象在整个迭代过程中保持不变。但是,正如预期的那样,str它引用的具体内容在第二个循环中有所不同。该cell对象引用animal,在get_petters()调用时创建。但是,在生成器函数运行时animal更改str它所指的对象。

在第一个循环中,在每次迭代期间,我们都创建了所有fs,但是只有在生成器get_petters()完全用尽并且list已经创建a 函数之后,才调用它们。

在第二个循环中,在每次迭代期间,我们暂停get_petters()生成器并f在每次暂停后调用。因此,我们最终animal在生成器功能暂停的那一刻检索了值。

正如@Claudiu对类似问题的回答

创建了三个单独的函数,但是每个函数都封闭了定义它们的环境-在这种情况下,是全局环境(如果将循环放在另一个函数内部,则为外部函数的环境)。不过,这确实是问题所在-在这种环境中,animal变量是突变的,并且所有的闭包都引用相同的animal

[编者注:i已更改为animal。]

Let’s simplify the question. Define:

def get_petters():
    for animal in ['cow', 'dog', 'cat']:
        def pet_function():
            return "Mary pets the " + animal + "."

        yield (animal, pet_function)

Then, just like in the question, we get:

>>> for name, f in list(get_petters()):
...     print(name + ":", f())

cow: Mary pets the cat.
dog: Mary pets the cat.
cat: Mary pets the cat.

But if we avoid creating a list() first:

>>> for name, f in get_petters():
...     print(name + ":", f())

cow: Mary pets the cow.
dog: Mary pets the dog.
cat: Mary pets the cat.

What’s going on? Why does this subtle difference completely change our results?


If we look at list(get_petters()), it’s clear from the changing memory addresses that we do indeed yield three different functions:

>>> list(get_petters())

[('cow', <function get_petters.<locals>.pet_function at 0x7ff2b988d790>),
 ('dog', <function get_petters.<locals>.pet_function at 0x7ff2c18f51f0>),
 ('cat', <function get_petters.<locals>.pet_function at 0x7ff2c14a9f70>)]

However, take a look at the cells that these functions are bound to:

>>> for _, f in list(get_petters()):
...     print(f(), f.__closure__)

Mary pets the cat. (<cell at 0x7ff2c112a9d0: str object at 0x7ff2c3f437f0>,)
Mary pets the cat. (<cell at 0x7ff2c112a9d0: str object at 0x7ff2c3f437f0>,)
Mary pets the cat. (<cell at 0x7ff2c112a9d0: str object at 0x7ff2c3f437f0>,)

>>> for _, f in get_petters():
...     print(f(), f.__closure__)

Mary pets the cow. (<cell at 0x7ff2b86b5d00: str object at 0x7ff2c1a95670>,)
Mary pets the dog. (<cell at 0x7ff2b86b5d00: str object at 0x7ff2c1a952f0>,)
Mary pets the cat. (<cell at 0x7ff2b86b5d00: str object at 0x7ff2c3f437f0>,)

For both loops, the cell object remains the same throughout the iterations. However, as expected, the specific str it references varies in the second loop. The cell object refers to animal, which is created when get_petters() is called. However, animal changes what str object it refers to as the generator function runs.

In the first loop, during each iteration, we create all the fs, but we only call them after the generator get_petters() is completely exhausted and a list of functions is already created.

In the second loop, during each iteration, we are pausing the get_petters() generator and calling f after each pause. Thus, we end up retrieving the value of animal at that moment in time that the generator function is paused.

As @Claudiu puts in an answer to a similar question:

Three separate functions are created, but they each have the closure of the environment they’re defined in – in this case, the global environment (or the outer function’s environment if the loop is placed inside another function). This is exactly the problem, though — in this environment, animal is mutated, and the closures all refer to the same animal.

[Editor note: i has been changed to animal.]


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

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

我已经在Python中看到并使用了嵌套函数,它们与闭包的定义匹配。那么为什么叫他们nested functions而不是closures

嵌套函数不是因为没有被外部世界使用而不是闭包吗?

更新:我正在阅读有关闭包的知识,这让我开始思考关于Python的这个概念。我搜索并找到了某人在下面的评论中提到的文章,但是我无法完全理解该文章中的解释,所以这就是为什么我问这个问题。

I have seen and used nested functions in Python, and they match the definition of a closure. So why are they called nested functions instead of closures?

Are nested functions not closures because they are not used by the external world?

UPDATE: I was reading about closures and it got me thinking about this concept with respect to Python. I searched and found the article mentioned by someone in a comment below, but I couldn’t completely understand the explanation in that article, so that is why I am asking this question.


回答 0

当函数可以从完成其执行的封闭范围访问局部变量时,就会发生关闭。

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

printer = make_printer('Foo!')
printer()

make_printer被调用时,一个新的帧放在堆栈上的编译代码的printer功能作为一个恒定的值和msg作为本地。然后,它创建并返回函数。由于函数printer引用了msg变量,因此在make_printer函数返回后,该变量将保持活动状态。

因此,如果您的嵌套函数没有

  1. 访问封闭范围本地的变量,
  2. 当它们在该范围之外执行时,

那么它们不是闭包。

这是一个不是闭包的嵌套函数的示例。

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

printer = make_printer("Foo!")
printer()  #Output: Foo!

在这里,我们将值绑定到参数的默认值。printer创建函数时会发生这种情况,因此返回 后无需保留对msgexternal 值的引用。在这种情况下只是函数的普通局部变量。printermake_printermsgprinter

A closure occurs when a function has access to a local variable from an enclosing scope that has finished its execution.

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

printer = make_printer('Foo!')
printer()

When make_printer is called, a new frame is put on the stack with the compiled code for the printer function as a constant and the value of msg as a local. It then creates and returns the function. Because the function printer references the msg variable, it is kept alive after the make_printer function has returned.

So, if your nested functions don’t

  1. access variables that are local to enclosing scopes,
  2. do so when they are executed outside of that scope,

then they are not closures.

Here’s an example of a nested function which is not a closure.

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

printer = make_printer("Foo!")
printer()  #Output: Foo!

Here, we are binding the value to the default value of a parameter. This occurs when the function printer is created and so no reference to the value of msg external to printer needs to be maintained after make_printer returns. msg is just a normal local variable of the function printer in this context.


回答 1

这个问题已经由 aaronasterling 回答

但是,可能有人会对变量如何在后台存储感兴趣。

在摘录之前:

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

  • 如果函数不使用自由变量,则不会形成闭包。

  • 如果还有另一个使用自由变量的内部级别- 所有以前的级别都保存词法环境(末尾的示例)

  • 功能属性func_closure蟒<3.X或__closure__在python> 3.X节省的自由变量。

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

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

>>> def foo():
...     def fii():
...         pass
...     return fii
...
>>> f = foo()
>>> f.func_closure
>>> 'func_closure' in dir(f)
True
>>>

注意:必须提供免费变量才能创建封包。

我将使用与上面相同的代码段进行说明:

>>> def make_printer(msg):
...     def printer():
...         print msg
...     return printer
...
>>> printer = make_printer('Foo!')
>>> printer()  #Output: Foo!

而且所有Python函数都有一个Closure属性,因此让我们检查与Closure函数关联的封闭变量。

这是func_closure函数的属性printer

>>> 'func_closure' in dir(printer)
True
>>> printer.func_closure
(<cell at 0x108154c90: str object at 0x108151de0>,)
>>>

closure属性返回单元格对象的元组,其中包含封闭范围中定义的变量的详细信息。

func_closure中的第一个元素可以是None或包含该函数的自由变量的绑定的单元格元组,并且是只读的。

>>> dir(printer.func_closure[0])
['__class__', '__cmp__', '__delattr__', '__doc__', '__format__', '__getattribute__',
 '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', 
 '__setattr__',  '__sizeof__', '__str__', '__subclasshook__', 'cell_contents']
>>>

在上面的输出中,您可以看到cell_contents,让我们看看它存储了什么:

>>> printer.func_closure[0].cell_contents
'Foo!'    
>>> type(printer.func_closure[0].cell_contents)
<type 'str'>
>>>

因此,当我们调用该函数时printer(),它访问存储在中的值cell_contents。这就是我们如何将输出显示为“ Foo!”。

我将再次解释使用上面的代码片段进行一些更改:

 >>> def make_printer(msg):
 ...     def printer():
 ...         pass
 ...     return printer
 ...
 >>> printer = make_printer('Foo!')
 >>> printer.func_closure
 >>>

在上面的代码片段中,我没有在打印机函数中打印msg,因此它不会创建任何自由变量。由于没有自由变量,因此闭包内部将没有内容。那就是我们上面看到的。

现在,我将解释另一个不同的片段,以清除一切Free VariableClosure

>>> def outer(x):
...     def intermediate(y):
...         free = 'free'
...         def inner(z):
...             return '%s %s %s %s' %  (x, y, free, z)
...         return inner
...     return intermediate
...
>>> outer('I')('am')('variable')
'I am free variable'
>>>
>>> inter = outer('I')
>>> inter.func_closure
(<cell at 0x10c989130: str object at 0x10c831b98>,)
>>> inter.func_closure[0].cell_contents
'I'
>>> inn = inter('am')

因此,我们看到一个func_closure属性是闭包单元格的元组,我们可以显式引用它们及其内容-一个单元格具有属性“ cell_contents”

>>> inn.func_closure
(<cell at 0x10c9807c0: str object at 0x10c9b0990>, 
 <cell at 0x10c980f68: str object at   0x10c9eaf30>, 
 <cell at 0x10c989130: str object at 0x10c831b98>)
>>> for i in inn.func_closure:
...     print i.cell_contents
...
free
am 
I
>>>

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

>>> inn('variable')
'I am free variable'
>>>

The question has already been answered by aaronasterling

However, someone might be interested in how the variables are stored under the hood.

Before coming to the snippet:

Closures are functions that inherit variables from their enclosing environment. When you pass a function callback as an argument to another function that will do I/O, this callback function will be invoked later, and this function will — almost magically — remember the context in which it was declared, along with all the variables available in that context.

  • If a function does not use free variables it doesn’t form a closure.

  • If there is another inner level which uses free variables — all previous levels save the lexical environment ( example at the end )

  • function attributes func_closure in python < 3.X or __closure__ in python > 3.X save the free variables.

  • Every function in python has this closure attributes, but it doesn’t save any content if there is no free variables.

example: of closure attributes but no content inside as there is no free variable.

>>> def foo():
...     def fii():
...         pass
...     return fii
...
>>> f = foo()
>>> f.func_closure
>>> 'func_closure' in dir(f)
True
>>>

NB: FREE VARIABLE IS MUST TO CREATE A CLOSURE.

I will explain using the same snippet as above:

>>> def make_printer(msg):
...     def printer():
...         print msg
...     return printer
...
>>> printer = make_printer('Foo!')
>>> printer()  #Output: Foo!

And all Python functions have a closure attribute so let’s examine the enclosing variables associated with a closure function.

Here is the attribute func_closure for the function printer

>>> 'func_closure' in dir(printer)
True
>>> printer.func_closure
(<cell at 0x108154c90: str object at 0x108151de0>,)
>>>

The closure attribute returns a tuple of cell objects which contain details of the variables defined in the enclosing scope.

The first element in the func_closure which could be None or a tuple of cells that contain bindings for the function’s free variables and it is read-only.

>>> dir(printer.func_closure[0])
['__class__', '__cmp__', '__delattr__', '__doc__', '__format__', '__getattribute__',
 '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', 
 '__setattr__',  '__sizeof__', '__str__', '__subclasshook__', 'cell_contents']
>>>

Here in the above output you can see cell_contents, let’s see what it stores:

>>> printer.func_closure[0].cell_contents
'Foo!'    
>>> type(printer.func_closure[0].cell_contents)
<type 'str'>
>>>

So, when we called the function printer(), it accesses the value stored inside the cell_contents. This is how we got the output as ‘Foo!’

Again I will explain using the above snippet with some changes:

 >>> def make_printer(msg):
 ...     def printer():
 ...         pass
 ...     return printer
 ...
 >>> printer = make_printer('Foo!')
 >>> printer.func_closure
 >>>

In the above snippet, I din’t print msg inside the printer function, so it doesn’t create any free variable. As there is no free variable, there will be no content inside the closure. Thats exactly what we see above.

Now I will explain another different snippet to clear out everything Free Variable with Closure:

>>> def outer(x):
...     def intermediate(y):
...         free = 'free'
...         def inner(z):
...             return '%s %s %s %s' %  (x, y, free, z)
...         return inner
...     return intermediate
...
>>> outer('I')('am')('variable')
'I am free variable'
>>>
>>> inter = outer('I')
>>> inter.func_closure
(<cell at 0x10c989130: str object at 0x10c831b98>,)
>>> inter.func_closure[0].cell_contents
'I'
>>> inn = inter('am')

So, we see that a func_closure property is a tuple of closure cells, we can refer them and their contents explicitly — a cell has property “cell_contents”

>>> inn.func_closure
(<cell at 0x10c9807c0: str object at 0x10c9b0990>, 
 <cell at 0x10c980f68: str object at   0x10c9eaf30>, 
 <cell at 0x10c989130: str object at 0x10c831b98>)
>>> for i in inn.func_closure:
...     print i.cell_contents
...
free
am 
I
>>>

Here when we called inn, it will refer all the save free variables so we get I am free variable

>>> inn('variable')
'I am free variable'
>>>

回答 2

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

闭包非常优雅,因为它使像这样编写的函数具有“内部记忆”的能力。从Python 2.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的范围限制,我将介绍一些方法。

1.使用global关键字(通常不建议使用)。

2.在Python 3.x中,使用nonlocal关键字(由@unutbu和@leewz建议)

3.定义一个简单的可修改类Object

class Object(object):
    pass

并创建一个 Object scope内部initCounter存储变量

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

    return counter

由于scope实际上仅是参考,因此对其字段执行的操作不会真正对其scope自身进行修改,因此不会出现错误。

4. @unutbu指出,另一种方法是将每个变量定义为数组(x = [0])并修改其第一个元素(x[0] += 1)。同样,不会发生错误,因为x它本身没有被修改。

5.根据@raxacoricofallapatorius的建议,您可以x设置counter

def initCounter ():

    def counter():
        counter.x += 1
        print counter.x

    counter.x = 0
    return counter

Python has a weak support for closure. To see what I mean take the following example of a counter using closure with 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

Closure is quite elegant since it gives functions written like this the ability to have “internal memory”. As of Python 2.7 this is not possible. If you try

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

count = initCounter();

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

You’ll get an error saying that x is not defined. But how can that be if it has been shown by others that you can print it? This is because of how Python it manages the functions variable scope. While the inner function can read the outer function’s variables, it cannot write them.

This is a shame really. But with just read-only closure you can at least implement the function decorator pattern for which Python offers syntactic sugar.

Update

As its been pointed out, there are ways to deal with python’s scope limitations and I’ll expose some.

1. Use the global keyword (in general not recommended).

2. In Python 3.x, use the nonlocal keyword (suggested by @unutbu and @leewz)

3. Define a simple modifiable class Object

class Object(object):
    pass

and create an Object scope within initCounter to store the variables

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

    return counter

Since scope is really just a reference, actions taken with its fields do not really modify scope itself, so no error arises.

4. An alternative way, as @unutbu pointed out, would be to define each variable as an array (x = [0]) and modify it’s first element (x[0] += 1). Again no error arises because x itself is not modified.

5. As suggested by @raxacoricofallapatorius, you could make x a property of counter

def initCounter ():

    def counter():
        counter.x += 1
        print counter.x

    counter.x = 0
    return counter

回答 3

Python 2没有闭包-它具有类似于闭包的解决方法。

答案中已经有很多示例-将变量复制到内部函数,修改内部函数上的对象等。

在Python 3中,支持更为明确-简洁:

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

用法:

start = closure()
start() # prints 1
start() # prints 2
start() # prints 3

nonlocal关键词结合的内函数来明确提到的外变量,实际上包围它。因此,更明确地说是“关闭”。

Python 2 didn’t have closures – it had workarounds that resembled closures.

There are plenty of examples in answers already given – copying in variables to the inner function, modifying an object on the inner function, etc.

In Python 3, support is more explicit – and succinct:

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

Usage:

start = closure()
start() # prints 1
start() # prints 2
start() # prints 3

The nonlocal keyword binds the inner function to the outer variable explicitly mentioned, in effect enclosing it. Hence more explicitly a ‘closure’.


回答 4

我遇到的情况是我需要一个单独的但持久的命名空间。我上过课。我不是这样。隔离但持久的名称是闭包。

>>> 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

I had a situation where I needed a separate but persistent name space. I used classes. I don’t otherwise. Segregated but persistent names are closures.

>>> 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

回答 5

def nested1(num1): 
    print "nested1 has",num1
    def nested2(num2):
        print "nested2 has",num2,"and it can reach to",num1
        return num1+num2    #num1 referenced for reading here
    return nested2

给出:

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

这是什么是闭包以及如何使用闭包的示例。

def nested1(num1): 
    print "nested1 has",num1
    def nested2(num2):
        print "nested2 has",num2,"and it can reach to",num1
        return num1+num2    #num1 referenced for reading here
    return nested2

Gives:

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

This is an example of what a closure is and how it can be used.


回答 6

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

JS:

function make () {
  var cl = 1;
  function gett () {
    console.log(cl);
  }
  function sett (val) {
    cl = val;
  }
  return [gett, sett]
}

并执行:

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关键字明确声明一个。

I’d like to offer another simple comparison between python and JS example, if this helps make things clearer.

JS:

function make () {
  var cl = 1;
  function gett () {
    console.log(cl);
  }
  function sett (val) {
    cl = val;
  }
  return [gett, sett]
}

and executing:

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

and executing:

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

Reason: As many others said above, in python, if there is an assignment in the inner scope to a variable with the same name, a new reference in the inner scope is created. Not so with JS, unless you explicitly declare one with the var keyword.