函数闭包 vs. 可调用类
在很多情况下,我们有两种实现的选择:闭包和可调用类。比如说,
class F:
def __init__(self, op):
self.op = op
def __call__(self, arg1, arg2):
if (self.op == 'mult'):
return arg1 * arg2
if (self.op == 'add'):
return arg1 + arg2
raise InvalidOp(op)
f = F('add')
或者
def F(op):
if op == 'or':
def f_(arg1, arg2):
return arg1 | arg2
return f_
if op == 'and':
def g_(arg1, arg2):
return arg1 & arg2
return g_
raise InvalidOp(op)
f = F('add')
在选择这两者时,有哪些因素需要考虑呢?
我能想到两个:
闭包的性能似乎总是更好(我想不出反例)。
我觉得有些情况下闭包无法完成任务(例如,当它的状态随着时间变化时)。
我这样说对吗?还有什么可以补充的吗?
6 个回答
我知道这个帖子有点旧了,但我想提一个没有提到的点,那就是在Python(在引入nonlocal之前)中,你不能修改引用环境中的局部变量。虽然在你的例子中这种修改并不重要,但从技术上讲,不能修改这样的变量意味着它不是真正的闭包。
举个例子,下面的代码是不能运行的:
def counter():
i = 0
def f():
i += 1
return i
return f
c = counter()
c()
上面调用c时会引发一个UnboundLocalError异常。
不过,这个问题可以通过使用可变对象,比如字典,来解决:
def counter():
d = {'i': 0}
def f():
d['i'] += 1
return d['i']
return f
c = counter()
c() # 1
c() # 2
当然,这只是一个变通办法。
我觉得用类来写代码一眼就能看懂,所以更容易维护。因为这是写好Python代码的一个基本原则,所以在其他条件相同的情况下,使用类比用嵌套函数要好。这就是Python灵活性的一种体现,有时候会让它违背“应该有一种,最好只有一种,明显的方式来做某件事”这个原则。
在性能方面,两者的差别应该不大。如果你的代码在这个层面上性能很重要,那你肯定需要对它进行性能分析,找出需要优化的部分,甚至可能需要把一些代码重写成原生代码。
不过,如果有一个紧密的循环在使用状态变量,那么评估闭包变量的速度应该会比评估类属性稍微快一点。当然,你可以在进入循环之前,在类的方法里加一行代码,比如 op = self.op
,这样在循环里的变量访问就变成了访问一个局部变量,这样就避免了每次访问都要查找属性的过程。再次强调,性能差别应该不大,如果你真的需要这么一点点额外的性能,而你又在用Python编程,那你可能还有更严重的问题。
闭包运行得更快。而类则更灵活(也就是说,除了 __call 方法,还有更多其他的方法可以使用)。