你能解释一下闭包 (与 Python 的关系) 吗?

2025-01-15 08:46:00
admin
原创
109
摘要:问题描述:我读了很多关于闭包的文章,我想我理解了它们,但是为了不让我自己和其他人混淆,我希望有人能尽可能简洁明了地解释闭包。我正在寻找一个简单的解释,可以帮助我理解在哪里以及为什么要使用它们。解决方案 1:关闭再关闭对象是附加了方法的数据,闭包是附加了数据的函数。def make_counter(): ...

问题描述:

我读了很多关于闭包的文章,我想我理解了它们,但是为了不让我自己和其他人混淆,我希望有人能尽可能简洁明了地解释闭包。我正在寻找一个简单的解释,可以帮助我理解在哪里以及为什么要使用它们。


解决方案 1:

关闭再关闭

对象是附加了方法的数据,闭包是附加了数据的函数。

def make_counter():
    i = 0
    def counter(): # counter() is a closure
        nonlocal i
        i += 1
        return i
    return counter

c1 = make_counter()
c2 = make_counter()

print (c1(), c1(), c2(), c2())
# -> 1 2 1 2

解决方案 2:

很简单:一个函数引用包含范围的变量,可能在控制流离开该范围之后。最后一点非常有用:

>>> def makeConstantAdder(x):
...     constant = x
...     def adder(y):
...         return y + constant
...     return adder
... 
>>> f = makeConstantAdder(12)
>>> f(3)
15
>>> g = makeConstantAdder(4)
>>> g(3)
7

请注意,12 和 4 分别在 f 和 g 内部“消失”,这个特征使得 f 和 g 成为适当的闭包。

解决方案 3:

说实话,我非常了解闭包,只是我一直搞不清楚“闭包”到底是什么,以及它为什么如此“闭包”。我建议你不要再寻找术语选择背后的任何逻辑了。

无论如何,这是我的解释:

def foo():
   x = 3
   def bar():
      print(x)
   x = 5
   return bar

bar = foo()
bar()   # print 5

这里的一个关键思想是,从 foo 返回的函数对象保留了对本地变量“x”的钩子,即使“x”已经超出范围并且应该失效。这个钩子是针对变量本身的,而不仅仅是 var 当时的值,因此当调用 bar 时,它会打印 5,而不是 3。

还需明确的是,Python 2.x 的闭包功能有限:我无法在“bar”中修改“x”,因为写入“x = bla”将在 bar 中声明本地“x”,而不是分配给 foo 的“x”。这是 Python 的 assignment=declaration 的副作用。为了解决这个问题,Python 3.0 引入了 nonlocal 关键字:

def foo():
   x = 3
   def bar():
      print x
   def ack():
      nonlocal x
      x = 7
   x = 5
   return (bar, ack)

bar, ack = foo()
ack()   # modify x of the call to foo
bar()   # print 7

解决方案 4:

我喜欢这个粗略、简洁的定义:

可以引用不再活跃的环境的功能。

我想补充一下

闭包允许你将变量绑定到函数中,而无需将它们作为参数传递

接受参数的装饰器是闭包的常见用途。闭包是这种“函数工厂”的常见实现机制。当策略在运行时被数据修改时,我经常选择在策略模式中使用闭包。

在允许匿名块定义的语言中(例如 Ruby、C#),闭包可用于实现(相当于)新颖的新控制结构。缺少匿名块是Python 中闭包的局限性之一。

解决方案 5:

我从未听说过事务在与解释闭包相同的上下文中使用,并且这里实际上没有任何事务语义。

之所以称为闭包,是因为它“封闭”了外部变量(常量)——也就是说,它不仅是一个函数,而且是创建该函数的环境的封闭部分。

在下面的例子中,在改变 x 之后调用闭包 g 也会改变 g 中 x 的值,因为 g 对 x 封闭:

x = 0

def f():
    def g(): 
        return x * 2
    return g


closure = f()
print(closure()) # 0
x = 2
print(closure()) # 4

解决方案 6:

# A Closure is a function object that remembers values in enclosing scopes even if they are not present in memory.

# Defining a closure

# This is an outer function.
def outer_function(message):
    # This is an inner nested function.
    def inner_function():
        print(message)
    return inner_function

# Now lets call the outer function and return value bound to name 'temp'
temp = outer_function("Hello")
# On calling temp, 'message' will be still be remembered although we had finished executing outer_function()
temp()
# Technique by which some data('message') that remembers values in enclosing scopes 
# even if they are not present in memory is called closures

# Output: Hello

关闭需要满足的标准是:

  1. 我们必须有嵌套函数。

  2. 嵌套函数必须引用封闭函数中定义的值。

  3. 封闭函数必须返回嵌套函数。


# Example 2
def make_multiplier_of(n): # Outer function
    def multiplier(x): # Inner nested function
        return x * n
    return multiplier
# Multiplier of 3
times3 = make_multiplier_of(3)
# Multiplier of 5
times5 = make_multiplier_of(5)
print(times5(3)) # 15
print(times3(2)) #  6

解决方案 7:

在 Python 中,闭包是具有不可变地绑定到它的变量的函数实例。

事实上,数据模型在其函数属性的描述中解释了这一点__closure__

无,或包含函数自由变量绑定的单元格元组。只读

为了证明这一点:

def enclosure(foo):
    def closure(bar):
        print(foo, bar)
    return closure

closure_instance = enclosure('foo')

显然,我们知道现在有一个由变量名指向的函数closure_instance。表面上看,如果我们用一个对象调用它,bar它应该打印字符串,'foo'以及的字符串表示形式bar

实际上,字符串“foo”绑定到函数的实例,我们可以在这里直接读取它,通过访问cell_contents属性元组中第一个(也是唯一一个)单元格的属性__closure__

>>> closure_instance.__closure__[0].cell_contents
'foo'

顺便说一下,单元格对象在 C API 文档中有描述:

“Cell”对象用于实现多个作用域引用的变量

我们可以演示闭包的用法,注意它'foo'卡在函数中并且不会改变:

>>> closure_instance('bar')
foo bar
>>> closure_instance('baz')
foo baz
>>> closure_instance('quux')
foo quux

没有什么可以改变它:

>>> closure_instance.__closure__ = None
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: readonly attribute

部分函数

给出的示例使用闭包作为部分函数,​​但如果这是我们唯一的目标,那么可以使用以下方法实现相同的目标functools.partial

>>> from __future__ import print_function # use this if you're in Python 2.
>>> partial_function = functools.partial(print, 'foo')
>>> partial_function('bar')
foo bar
>>> partial_function('baz')
foo baz
>>> partial_function('quux')
foo quux

还有更复杂的闭包不适合部分函数示例,我会在时间允许的情况下进一步演示它们。

解决方案 8:

这是闭包的一个典型用例 - GUI 元素的回调(这将成为按钮类的子类化的一种替代方法)。例如,您可以构造一个函数,该函数将在按钮按下时被调用,并“关闭”父作用域中处理点击所需的相关变量。这样,您就可以从同一个初始化函数连接相当复杂的接口,将所有依赖项构建到闭包中。

解决方案 9:

以下是 Python3 闭包的示例

def closure(x):
    def counter():
        nonlocal x
        x += 1
        return x
    return counter;

counter1 = closure(100);
counter2 = closure(200);

print("i from closure 1 " + str(counter1()))
print("i from closure 1 " + str(counter1()))
print("i from closure 2 " + str(counter2()))
print("i from closure 1 " + str(counter1()))
print("i from closure 1 " + str(counter1()))
print("i from closure 1 " + str(counter1()))
print("i from closure 2 " + str(counter2()))

# result

i from closure 1 101
i from closure 1 102
i from closure 2 201
i from closure 1 103
i from closure 1 104
i from closure 1 105
i from closure 2 202

解决方案 10:

我们都使用过Python 中的装饰器。它们是展示 Python 中什么是闭包函数的很好的例子。

class Test():
    def decorator(func):
        def wrapper(*args):
            b = args[1] + 5
            return func(b)
        return wrapper

@decorator
def foo(val):
    print val + 2

obj = Test()
obj.foo(5)

最终值为 12

这里,包装函数能够访问 func 对象,因为包装函数是“词法闭包”,它可以访问其父级属性。这就是为什么它能够访问 func 对象。

解决方案 11:

我想分享我的例子和关于闭包的解释。我做了一个python示例和两个图来演示堆栈状态。

def maker(a, b, n):
    margin_top = 2
    padding = 4
    def message(msg):
        print('
’ * margin_top, a * n, 
            ' ‘ * padding, msg, ' ‘ * padding, b * n)
    return message

f = maker('*', '#', 5)
g = maker('', '♥’, 3)
…
f('hello')
g(‘good bye!')

该代码的输出如下:

*****      hello      #####

      good bye!    ♥♥♥

下面两张图展示了堆栈和附加到函数对象的闭包。

当函数从 maker 返回时

当函数稍后被调用时

当通过参数或非局部变量调用函数时,代码需要局部变量绑定,例如 margin_top、padding 以及 a、b、n。为了确保函数代码正常工作,很久以前就消失了的 maker 函数的堆栈框架应该是可访问的,它可以在我们可以与“消息”函数对象一起找到的闭包中备份。

解决方案 12:

Python 中的闭包是指即使在外部函数执行完毕后,仍保留对外部(封闭)作用域变量的访问权的函数。简而言之,闭包会“封闭”或从其周围环境中捕获所需的变量,从而让您以优雅而高效的方式维护状态信息。

例子:

def multiplier(factor):
    def multiply(x):
        return x * factor
    return multiply

# Usage
double = multiplier(2)
triple = multiplier(3)

print(double(5))  # Output: 10
print(triple(5))  # Output: 15

解决方案 13:

对我来说,“闭包”是能够记住创建环境的函数。此功能允许您在闭包中使用变量或方法,否则您将无法使用它们,因为它们不再存在或由于范围而无法访问。让我们看看 Ruby 中的这段代码:

def makefunction (x)
  def multiply (a,b)
    puts a*b
  end
  return lambda {|n| multiply(n,x)} # => returning a closure
end

func = makefunction(2) # => we capture the closure
func.call(6)    # => Result equal "12"  

即使“乘法”方法和“x”变量都不再存在,它仍然有效。这一切都是因为闭包能力可以记住。

解决方案 14:

这是我最近在 LinkedIn 上发布的关于结束的帖子。希望它和图片一起能有所帮助。

帖子链接:Python 中的闭包

有时,

  • 你想要一个函数来保留对变量的访问

  • 即使其父函数已经执行完毕!

这是怎么回事?

进入

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用