Nir*_*man 8 ruby python closures functor
我最近潜入Python.以前,我用C++和Matlab编写了大部分数值和数据分析代码.我看到了很多关于Python和Ruby以及闭包的讨论.几乎所有的例子都是这样的:
>>> def makeAdder(y):
... def myAdder(x):
... return x + y
... return myAdder
...
>>> f = makeAdder(10)
>>> f(5)
15
Run Code Online (Sandbox Code Playgroud)
我知道这在某种意义上是有用的.但是,实际上,在这种情况下("只读"情况)的行为可以很容易地被对象(仿函数)模拟:
>>> class MyAdder(object):
... def __init__(self,y):
... self.y = y
... def __call__(self,x):
... return self.y + x
...
>>> f = MyAdder(5)
>>> f(10)
15
Run Code Online (Sandbox Code Playgroud)
该对象不会占用更多的代码空间,而且它的用途更广泛.跟踪和调试后续代码也更容易.
在这种情况下,我们只读取非局部变量.但是我们也可以写它:在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
Run Code Online (Sandbox Code Playgroud)
至少对我来说,这种行为是不直观的.它还有可能导致内存泄漏.这会让你有很多绳子可以自己挂起来.同样,在Ruby中尤其如此,您不需要显式地启用它(与Python不同),并且因为在Ruby中,所有主代码都可以访问所有内容.如果外部变量因为处于闭包中而发生变化,那么如果你绕过那个闭包,你可以让变量无限期地改变,并且远离它所居住的地方.与始终安全地携带数据的对象形成对比.
为什么你会听到很多关于闭包有多好的讨论,以及它们应该如何被包含在Java中,如果它们没有完全在Python中,它是如何吸收的?等等?为什么不使用仿函数?或重构代码以避免,因为它们有多么危险?只是为了澄清一下,我不是那些嘴巴OO类型的泡沫之一.我是否低估了他们的使用,夸大了他们的危险,或两者兼而有之?
编辑:也许我应该区分三件事:只读一次的闭包(这是我的例子显示的,几乎每个人都讨论过),一般读的闭包,以及写的闭包.如果你使用外部函数的局部变量在另一个函数中定义一个函数,那么它几乎不会再回来困扰你.该空间中的变量无法以我能想到的任何方式访问,因此您无法更改它.这是非常安全的,并且是一种方便(可能比仿函数)方式生成函数.另一方面,如果在类方法内或主线程内创建闭包,则每次调用时都会读入变量,可以从其他位置访问.所以它可以改变.我认为这是危险的,因为关闭的变量不会出现在函数头中.您可以在代码的第1页上说一个关闭主线程变量x的长闭包,然后根据无关原因修改x.然后重新使用闭包并获得您不理解的奇怪行为,这可能很难调试.如果您实际上写入封闭变量,那么正如我在Ruby中的示例所示,您确实有可能造成混乱并导致意外行为.
Edit2:我举了一个关于第三种用法的闭包奇怪行为的例子,写入非局部变量.这是第二种用法中的奇怪(并非坏)行为的示例(在范围中定义闭包变量可以修改的闭包):
>>> fs = [(lambda n: i + n) for i in range(10)]
>>> fs[4](5)
14
Run Code Online (Sandbox Code Playgroud)
Gar*_*tty 10
可读性.您的Python示例显示了闭包版本与仿函数相比更加明显和易于阅读的程度.
我们还巧妙地避免创建一个什么都不做但只是像函数一样的类 - 这种冗余的味道.
如果不出意外,当我们正在做的事情,是有意义的把它描述为一个动作,而不是一个对象.
作为另注,在这些结构使用的例子很多在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
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
3815 次 |
最近记录: |