简体   繁体   English

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

[英]Why aren't python nested functions called closures?

I have seen and used nested functions in Python, and they match the definition of a closure.我在 Python 中看到并使用过嵌套函数,它们与闭包的定义相匹配。 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.更新:我正在阅读关于闭包的文章,它让我想到了关于 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.我在下面的评论中搜索并找到了某人提到的文章,但我无法完全理解该文章中的解释,所以这就是我问这个问题的原因。

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.make_printer被调用时,一个新的帧被放置在堆栈上,将printer函数的编译代码作为常量,将msg的值作为本地值。 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.因为函数printer引用了msg变量,所以它在make_printer函数返回后保持活动状态。

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.当创建函数printer时会发生这种情况,因此在make_printer返回后make_printer维护对printer外部的msg值的引用。 msg is just a normal local variable of the function printer in this context.在这种情况下, msg只是函数printer一个普通局部变量。

The question has already been answered by aaronasterling 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.当你将一个函数回调作为参数传递给另一个将执行 I/O 的函数时,这个回调函数将在稍后被调用,并且这个函数将——几乎神奇地——记住它被声明的上下文,以及所有可用的变量在这种情况下。

  • 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.函数属性func_closure in python < 3.X or __closure__ in python > 3.X 保存自由变量。

  • Every function in python has the closure attribute, but if there are no free variables, it is empty. python中的每个函数都有closure属性,但如果没有自由变量,则为空。

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.并且所有 Python 函数都有一个闭包属性,所以让我们检查与闭包函数关联的封闭变量。

Here is the attribute func_closure for the function printer这是函数printer的属性func_closure

>>> '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. closure属性返回一个单元格对象元组,其中包含在封闭范围内定义的变量的详细信息。

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. 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']
>>>

Here in the above output you can see cell_contents , let's see what it stores:在上面的输出中,您可以看到cell_contents ,让我们看看它存储了什么:

>>> 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 .因此,当我们调用函数printer() ,它访问存储在cell_contents的值。 This is how we got the output as 'Foo!'这就是我们如何得到“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 didn't print msg inside the printer function, so it doesn't create any free variable.在上面的代码片段中,我没有在打印机函数中打印 msg,所以它不会创建任何自由变量。 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 :现在我将解释另一个不同的片段,以清除所有带有Closure Free Variable

>>> 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"所以,我们看到一个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
>>>

Here when we called inn , it will refer all the save free variables so we get I am free variable在这里,当我们调用inn ,它将引用所有保存的自由变量,因此我们得到I am free variable

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

Python has a weak support for closure. Python 对闭包的支持很弱 To see what I mean take the following example of a counter using closure with JavaScript:要了解我的意思,请看以下使用 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.从 Python 2.7 开始,这是不可能的。 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.您会收到一条错误消息,指出 x 未定义。 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.这是因为 Python 如何管理函数变量范围。 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.但是只要使用只读闭包,您至少可以实现 Python 为其提供语法糖的函数装饰器模式

Update更新

As its been pointed out, there are ways to deal with python's scope limitations and I'll expose some.正如它所指出的,有一些方法可以处理 python 的范围限制,我将公开一些。

1. Use the global keyword (in general not recommended). 1.使用global关键字(一般不推荐)。

2. In Python 3.x, use the nonlocal keyword (suggested by @unutbu and @leewz) 2.在Python 3.x中,使用nonlocal关键字(通过@unutbu和@leewz建议的)

3. Define a simple modifiable class Object 3.定义一个简单的可修改类Object

class Object(object):
    pass

and create an Object scope within initCounter to store the variables并在initCounter创建一个Object scope来存储变量

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.由于scope实际上只是一个引用,因此对其字段采取的操作不会真正修改scope本身,因此不会出现错误。

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 ). 4.正如@unutbu 指出的那样,另一种方法是将每个变量定义为一个数组 ( x = [0] ) 并修改它的第一个元素 ( x[0] += 1 )。 Again no error arises because x itself is not modified.同样没有错误出现,因为x本身没有被修改。

5. As suggested by @raxacoricofallapatorius, you could make x a property of counter 5.正如@raxacoricofallapatorius 所建议的,你可以让x成为counter一个属性

def initCounter ():

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

    counter.x = 0
    return counter

Python 2 didn't have closures - it had workarounds that resembled closures. Python 2 没有闭包——它有类似于闭包的变通方法。

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:在 Python 3 中,支持更加明确 - 并且简洁:

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

Usage:用法:

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

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

another() # print 1

start() # prints 3

The nonlocal keyword binds the inner function to the outer variable explicitly mentioned, in effect enclosing it. nonlocal关键字将内部函数绑定到显式提到的外部变量,实际上是将它封闭起来。 Hence more explicitly a 'closure'.因此更明确地是“关闭”。

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
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.这是一个闭包是什么以及如何使用它的例子。

People are confusing about what closure is.人们对什么是闭包感到困惑。 Closure is not the inner function.闭包不是内部功能。 the meaning of closure is act of closing.关闭的意思是关闭的行为。 So inner function is closing over a nonlocal variable which is called free variable.所以内部函数正在关闭一个称为自由变量的非局部变量。

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

when you call counter_in() this will return inc function which has a free variable initial_value .当您调用counter_in()这将返回具有自由变量initial_value inc函数。 So we created a CLOSURE.所以我们创建了一个 CLOSURE。 people call inc as closure function and I think this is confusing people, people think "ok inner functions are closures".人们称inc为闭包函数,我认为这让人们感到困惑,人们认为“好的内部函数是闭包”。 in reality inc is not a closure, since it is part of the closure, to make life easy, they call it closure function.实际上inc不是闭包,因为它是闭包的一部分,为了让生活更轻松,他们称之为闭包函数。

  myClosingOverFunc=counter_in(2)

this returns inc function which is closing over the free variable initial_value .这将返回正在关闭自由变量initial_value inc函数。 when you invoke myClosingOverFunc当你调用myClosingOverFunc

 myClosingOverFunc() 

it will print 2.它将打印 2。

when python sees that a closure sytem exists, it creates a new obj called CELL.当 python 发现一个闭包系统存在时,它会创建一个名为 CELL 的新对象。 this will store only the name of the free variable which is initial_value in this case.这将仅存储自由变量的名称,在这种情况下为initial_value This Cell obj will point to another object which stores the value of the initial_value .这个 Cell obj 将指向另一个存储initial_value值的对象。

in our example, initial_value in outer function and inner function will point to this cell object, and this cell object will be point to the value of the initial_value .在我们的例子中,外部函数和内部函数中的initial_value将指向这个单元格对象,而这个单元格对象将指向initial_value的值。

  variable initial_value =====>> CELL ==========>> value of initial_value

So when you call counter_in its scope is gone, but it does not matter.因此,当您调用counter_in它的作用域消失了,但这无关紧要。 because variable initial_value is directly referencing the CELL Obj.因为变量initial_value直接引用 CELL Obj。 and it indirectly references the value of initial_value .并且它间接引用了initial_value的值。 That is why even though scope of outer function is gone, inner function will still have access to the free variable这就是为什么即使外部函数的作用域消失了,内部函数仍然可以访问自由变量

let's say I want to write a function, which takes in a function as an arg and returns how many times this function is called.假设我想编写一个函数,它接受一个函数作为 arg 并返回该函数被调用的次数。

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
      

in this example cnt is our free variable and inner + cnt create CLOSURE.在这个例子中, cnt是我们的自由变量, inner + cnt创建 CLOSURE。 when python sees this it will create a CELL Obj and cnt will always directly reference this cell obj and CELL will reference the another obj in the memory which stores the value of cnt .当python看到这一点时,它将创建一个 CELL Obj 并且cnt将始终直接引用此单元格 obj 并且 CELL 将引用存储cnt值的内存中的另一个 obj 。 initially cnt=0.最初 cnt=0。

 cnt   ======>>>>  CELL  =============>  0

when you invoke the inner function wih passing a parameter counter(myFunc)() this will increase the cnt by 1. so our referencing schema will change as follow:当您通过传递参数counter(myFunc)()调用内部函数时,这将使 cnt 增加 1。因此我们的引用模式将更改如下:

 cnt   ======>>>>  CELL  =============>  1  #first counter(myFunc)()
 cnt   ======>>>>  CELL  =============>  2  #second counter(myFunc)()
 cnt   ======>>>>  CELL  =============>  3  #third counter(myFunc)()

this is only one instance of closure.这只是关闭的一个实例。 You can create multiple instances of closure with passing another function您可以通过传递另一个函数来创建多个闭包实例

counter(differentFunc)()

this will create a different CELL obj from the above.这将创建一个与上述不同的 CELL obj。 We just have created another closure instance.我们刚刚创建了另一个闭包实例。

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


  

I'd like to offer another simple comparison between python and JS example, if this helps make things clearer.如果这有助于使事情更清晰,我想提供 python 和 JS 示例之间的另一个简单比较。

JS: 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.原因:正如上面许多人所说,在python中,如果在内部作用域中对同名变量进行了赋值,则会在内部作用域中创建一个新的引用。 Not so with JS, unless you explicitly declare one with the var keyword. JS 并非如此,除非您使用var关键字明确声明。

For the readers of Structure and Interpretation of Computer Programs (SICP): there are 2 unrelated meanings of closure (CS VS Math), see Wikipedia for the latter/less common one:对于计算机程序的结构和解释 (SICP) 的读者:闭包2 种不相关的含义(CS VS 数学),请参阅 Wikipedia 了解后者/不太常见的含义:

Sussman and Abelson also use the term closure in the 1980s with a second, unrelated meaning: the property of an operator that adds data to a data structure to also be able to add nested data structures. Sussman 和Abelson在 1980 年代还使用了术语闭包,具有第二个不相关的含义:将数据添加到数据结构以也能够添加嵌套数据结构的运算符的属性。 This usage of the term comes from the mathematics usage rather than the prior usage in computer science.该术语的这种用法来自数学用法,而不是计算机科学中的先前用法。 The authors consider this overlap in terminology to be "unfortunate."作者认为这种术语重叠是“不幸的”。

The second (mathematical) meaning is also used in SICP in Python, see for example the discussion of tuples第二个(数学)含义也用于 SICP 中的 Python,例如参见元组的讨论

Our ability to use tuples as the elements of other tuples provides a new means of combination in our programming language.我们将元组用作其他元组的元素的能力为我们的编程语言提供了一种新的组合方式。 We call the ability for tuples to nest in this way a closure property of the tuple data type.我们将元组以这种方式嵌套的能力称为元组数据类型的闭包属性 In general, a method for combining data values satisfies the closure property if the result of combination can itself be combined using the same method.一般来说,如果组合结果本身可以使用相同的方法组合,则组合数据值的方法满足闭包属性。

Here is presented a way to identify if a function is a closure or not via code objects.这里提供了一种通过code对象识别 function 是否是闭包的方法。

As already mentioned in other answers, not every nested function is a closure.正如其他答案中已经提到的,并非每个嵌套的 function 都是闭包。 Given a composite function (which represent the overall action) its intermediate states can be either be a closure or a nested function.给定一个复合 function(代表整体动作),它的中间状态可以是闭包或嵌套的 function。 A closure is a kind function which is "parametrized" by its (non-empty) enclosing scope, the space of free-variables.闭包是一种 function,它通过其(非空)封闭 scope(自由变量空间)“参数化”。 Notice that a composite function may be made by both types.请注意,复合 function 可能由两种类型制成。

The (Python's) internal type code object represents the compiled function body. (Python 的)内部类型code object 代表编译后的 function 主体。 Its attribute co_cellvars and co_freevars can be used to "lookaround" the closure/scope of a function.其属性co_cellvarsco_freevars可用于“环顾” function 的闭包/范围。 As mentioned in the doc文档中所述

  • co_freevars : tuple of names of free variables (referenced via a function's closure) co_freevars :自由变量名称的元组(通过函数的闭包引用)
  • co_cellvars : tuple of names of cell variables (referenced by containing scopes). co_cellvars :单元格变量名称的元组(由包含范围引用)。

Once the function is read, by performing recursive calls a partial function is returned with its own __closure__ (hence cell_contents ) and a list of free-variables from its clousre and in its scope.读取 function 后,通过执行递归调用,返回部分 function 及其自己的__closure__ (因此cell_contents )以及来自其 clousre 和其 Z231A1FD14EC0BEA18A51A1FD21EC0BEA18A51A1FD140BEA18A51A1FD140BEA18 的自由变量列表。

Let introduce some support functions让我们介绍一些支持功能

# the "lookarounds"
def free_vars_from_closure_of(f):
    print(f.__name__, 'free vars from its closure',  f.__code__.co_cellvars)

def free_vars_in_scopes_of(f):
    print(f.__name__, 'free vars in its scope    ', f.__code__.co_freevars)

# read cells values
def cell_content(f):
    if f.__closure__ is not None:
        if len(f.__closure__) == 1: # otherwise problem with join
            c = f.__closure__[0].cell_contents
        else:
            c = ','.join(str(c.cell_contents) for c in f.__closure__)
    else:
        c = None

    print(f'cells of {f.__name__}: {c}')

Here an example from another answer rewritten in a more systematic way这里以更系统的方式重写了另一个答案的示例

def f1(x1):
    def f2(x2):
        a = 'free' # <- better choose different identifier to avoid confusion
        def f3(x3):
            return '%s %s %s %s' %  (x1, x2, a, x3)
        return f3
    return f2

# partial functions
p1 = f1('I')
p2 = p1('am')

# lookaround
for p in (f1, p1, p2):
    free_vars_in_scopes_of(p)
    free_vars_from_closure_of(p)
    cell_content(p)

Output Output

f1 free vars in its scope     ()         # <- because it's the most outer function
f1 free vars from its closure ('x1',)
cells of f1: None
f2 free vars in its scope     ('x1',)
f2 free vars from its closure ('a', 'x2')
cells of f2: I
f3 free vars in its scope     ('a', 'x1', 'x2')
f3 free vars from its closure ()        # <- because it's the most inner function
cells of f3: free, I, am

The lambda counterpart: lambda对应:

def g1(x1):
    return lambda x2, a='free': lambda x3: '%s %s %s %s' %  (x1, x2, a, x3)

From the point of view of the free variables/scoping are equivalent.从自由变量/作用域的角度来看是等价的。 The only minor differences are some values of some attributes of the code object: co_varnames , co_consts , co_code , co_lnotab , co_stacksize ... and natuarlly the __name__ attribute.唯一的细微差别是code object 的某些属性的一些值: co_varnamesco_constsco_codeco_lnotabco_stacksize ... 和自然的__name__属性。


A mixed example, closures and not at once:一个混合的例子,关闭而不是一次:

# example: counter
def h1():             # <- not a closure
    c = 0
    def h2(c=c):      # <- not a closure
        def h3(x):    # <- closure
            def h4(): # <- closure
                nonlocal c
                c += 1
                print(c)
            return h4
        return h3
    return h2

# partial functions
p1 = h1()
p2 = p1()
p3 = p2('X')

p1() # do nothing
p2('X') # do nothing
p2('X') # do nothing
p3() # +=1
p3() # +=1
p3() # +=1

# lookaround
for p in (h1, p1, p2, p3):
    free_vars_in_scopes_of(p)
    #free_vars_from_closure_of(p)
    cell_content(p)

Output Output

1 X
2 X
3 X
h1 free vars in its scope     ()
cells of h1: None
h2 free vars in its scope     ()
cells of h2: None
h3 free vars in its scope     ('c',)
cells of h3: 3
h4 free vars in its scope     ('c', 'x')
cells of h4: 3,X

h1 and h2 are both not closures since they have no cell and no free-variables in their scope. h1h2都不是闭包,因为它们的 scope 中没有单元格和自由变量。 h3 and h3 are closures and share (in this case) the same cell and free-variable for c . h3h3闭包并且共享(在这种情况下)相同的单元格和自由变量c h4 has a further free-variable x with its own cell. h4还有一个带有自己的单元的自由变量x


Final considerations:最后的考虑:

  • the __closure__ attribute and __code__.co_freevars can be used to check for values and names (identifiers) of the free-variables __closure__属性和__code__.co_freevars可用于检查自由变量的值和名称(标识符)
  • anti-analogies (in a broad sense) between nonlocal and __code__.co_cellvars : nonlocal acts towards the outer function, __code__.co_cellvars instead towards the internal function nonlocal 和nonlocal __code__.co_cellvars之间的反类比(广义上):nonlocal 作用于外部nonlocal__code__.co_cellvars而不是内部 function

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM