python - 闭包:什么是好的用例示例?为什么不是仿函数?它值得负面影响吗?

标签 python ruby closures functor

我最近开始研究 Python。以前,我主要用 C++ 和 Matlab 编写数值和数据分析代码。我看到很多关于 Python 和 Ruby 以及闭包的讨论。几乎所有的例子都是这样的:

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

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

>>> class MyAdder(object):
...  def __init__(self,y):
...   self.y = y
...  def __call__(self,x):
...   return self.y + x
... 
>>> f = MyAdder(5)
>>> f(10)
15

该对象不会占用更多的编码空间,而且用途更广。跟踪和调试后续代码也容易得多。

在这种情况下,我们只读取非局部变量。但我们也可以写入:在 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 中尤其如此,您不需要显式启用它(与 Python 不同),因为在 Ruby 中,主要代码中都有 block ,可以访问所有内容。如果外部变量由于处于闭包中而发生更改,如果您传递该闭包,则可以使变量无限期地更改,并且超出其所在位置的范围。与始终安全地携带其数据的对象形成对比。

为什么你会听到很多关于闭包有多好,它们应该如何潜在地包含在 Java 中,当它们没有完全在 Python 中时它是如何糟糕等等的讨论?为什么不使用仿函数?或者重构代码来避免,因为它们可能非常危险?澄清一下,我不是那种口吐白沫的 OO 类型。我是否低估了它们的用途,高估了它们的危险,或者两者兼而有之?

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

Edit2:我给出了第三种用法闭包的奇怪行为示例,写入非局部变量。这是第二次使用(在可以修改其封闭变量的范围内定义闭包)的奇怪(不是那么糟糕)行为的示例:

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

最佳答案

可读性。您的 Python 示例显示闭包版本与仿函数相比更加明显和易于阅读。

我们还巧妙地避免创建一个除了函数之外什么都不做的类——这有点冗余的味道。

如果不出意外,当我们某事时,it makes sense to describe it as an action, not an object .

另外请注意,装饰器是 大量 在 Python 中使用这些结构并产生巨大影响的示例。大多数函数装饰器都做这样的事情,它们是一个非常有用的特性。

编辑:关于状态的说明,请记住函数在 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

关于python - 闭包:什么是好的用例示例?为什么不是仿函数?它值得负面影响吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14045403/

相关文章:

ruby-on-rails - 如何在 Ruby on Rails 中读取基于编码的 CSV

javascript - 从回调函数中获取变量

Scala本地返回?

python - 根据条件从现有数据框创建新数据框

python - 有没有办法捕获站点使用 python 进行的网络调用?

ruby-on-rails - rails : How to make recent blog posts appear first

ruby-on-rails - Rails 异常不适用于 find_by 查询

java - 仅设置如果依赖于Gradle插件任务

python - wxPython ListCtrl 在调整大小时换行

python - 在 Python 中调用 URL 时出错