闭包:什么是一个好的用例示例?为什么不是函子?它值得否定吗?

2024-10-08 19:23:17 发布

您现在位置:Python中文网/ 问答频道 /正文

我最近喜欢上了Python。以前,我在C++和Matlab中主要编写了数值分析和数据分析代码。我看到了很多关于Python、Ruby和闭包的讨论。几乎所有的例子都是这样的:

>>> def makeAdder(y):
...  def myAdder(x):
...   return x + y
...  return myAdder
... 
>>> f = makeAdder(10)
>>> f(5)
15

我知道这在某种意义上是有用的。然而,实际上,在这种情况下(“只读”的情况下)的行为很容易被对象(函子)模拟:

^{pr2}$

不需要太多的空间,也不需要更多的代码。跟踪和调试后续代码也容易得多。在

在本例中,我们只读取非局部变量。但是我们也可以写它:在Ruby中,当然,在Python中,使用nonlocal关键字。当然,对象也支持这一点。但是对于对象,您将数据捆绑在一起,这样您就可以确切地知道发生了什么。闭包可能以完全不透明的方式携带变量,这可能导致代码非常难以调试。这里有一个非常奇怪的例子:

irb(main):001:0> def new_counter
irb(main):002:1> x = 0
irb(main):003:1> lambda { x +=1 }
irb(main):004:1> end
=> nil
irb(main):005:0> counter_a = new_counter
=> #<Proc:0x00007f85c6421cd0@(irb):3>
irb(main):006:0> counter_a.call
=> 1
irb(main):007:0> counter_a.call
=> 2

至少对我来说,这种行为是不直观的。它也有可能导致内存泄漏。这给了你一个巨大的绳子吊死自己。同样,这在Ruby中尤其如此,因为在Ruby中,你不需要显式地启用它(不像Python),因为在Ruby中,主代码上都有块,它们可以访问所有的东西。如果一个外部变量由于处于一个闭包中而发生了变化,如果你把这个闭包传递出去,你就可以让一个变量无限期地发生变化,并且远离它所在的位置。与总是安全携带数据的对象相比。在

为什么你会听到很多关于闭包有多好,它们应该如何被潜在地包含在Java中,当它们没有完全用Python编写时,它是如何糟糕的等等?为什么不用函子呢?或者重构代码以避免,因为它们有多么危险?我只是想澄清一下,我可不是那种“口吐白沫”的OO类型。我是低估了它们的使用,夸大了它们的危险,还是两者兼而有之?在

编辑:也许我应该区分三件事:只读取一次的闭包(这是我的例子所展示的,几乎所有人都在讨论)、一般读取的闭包和写的闭包。如果你在另一个函数内部定义了一个函数,使用一个外部函数的局部变量,这个函数几乎不可能再困扰你了。在那个空间里的变量是不可访问的,所以你不能改变它。这是非常安全的,也是生成函数的一种方便(可能不止函子)的方法。 另一方面,如果在类方法或主线程内创建一个闭包,则每次调用时它都会读入可以从其他地方访问的变量。所以它可以改变。我认为这很危险,因为closed over变量没有出现在函数头中。您可以说在代码的第1页上有一个长的闭包,它在一个主线程变量x上关闭,然后出于不相关的原因修改x。然后重复使用闭包,得到你不理解的奇怪行为,这可能很难调试。 如果你真的写封闭变量,那么正如我用Ruby的例子所显示的那样,你真的有可能把事情搞得一团糟,并导致意想不到的行为。在

Edit2:我在第三次使用闭包时给出了一个奇怪行为的例子,即写入非局部变量。下面是第二个用法(在可以修改closed over变量的作用域中定义闭包)的奇怪(不算坏)行为的示例:

>>> fs = [(lambda n: i + n) for i in range(10)]
>>> fs[4](5)
14

Tags: 对象函数代码returnmaindefcounter情况
1条回答
网友
1楼 · 发布于 2024-10-08 19:23:17

可读性。您的Python示例显示了闭包版本与functor相比有多明显和容易阅读。在

我们还巧妙地避免生成一个只做函数的类——这有冗余的味道。在

如果没有别的,当我们在做某事时,it makes sense to describe it as an action, not an object。在

另一个值得注意的是,在Python中,这些结构的一个很好的例子是decorators。大多数函数修饰符都是这样做的,它们是一个非常有用的特性。在

编辑:关于状态,请记住函数在Python中并不特殊,它们仍然是对象:

>>> def makeAdder(y):
...     def myAdder(x):
...         return x + myAdder.y
...     myAdder.y = y
...     return myAdder
... 
>>> f = makeAdder(10)
>>> f(5)
15
>>> f.y
10

相关问题 更多 >

    热门问题