对象还是闭包 - 何时使用?
我可以定义一个对象,并给它添加属性和方法:
class object:
def __init__(self,a,b):
self.a = a
self.b = b
def add(self):
self.sum = self.a + self.b
def subtr(self):
self.fin = self.sum - self.b
def getpar(self):
return self.fin
obj = object(2,3)
obj.add()
obj.subtr()
obj.getpar()
或者通过定义一个闭包来实现相同的功能:
def closure(a,b):
par = {}
def add():
par.update({'sum':a+b})
def subtr():
par.update({'fin':par['sum']-b})
def getpar():
return par['fin']
return {'add':add,'subtr':subtr,'getpar':getpar}
clos = closure(2,3)
clos['add']()
clos['subtr']()
clos['getpar']()
我觉得对象的写法对大多数人来说看起来更整洁,但有没有什么情况是使用闭包更合适的呢?
5 个回答
我觉得使用闭包的唯一真正原因就是,如果你想要一个相当强的保证,确保使用你对象或闭包的人无法访问到一个隐藏的变量。
比如说:
class Shotgun:
def __init__(self):
me = {}
me['ammo'] = 2
def shoot():
if me['ammo']:
me['ammo'] -= 1
print "BANG"
else:
print "Click ..."
self.shoot = shoot
s = Shotgun()
s.shoot()
s.shoot()
s.shoot()
你应该选择最能清楚表达你想要实现的版本。
在这个例子中,我觉得用对象的方式更清晰,因为它看起来是在模拟一个状态会变化的对象。看使用这个值的代码时,对象的版本似乎更能表达出明确的意图,而闭包的版本则有一些操作(比如索引和一些“魔法”字符串)显得有些多余。
在Python中,如果需要的东西更像是一个函数,并且可能需要捕捉一些状态,我会更倾向于使用闭包的方式。
def tag_closure(singular, plural):
def tag_it(n):
if n == 1:
return "1 " + singular
else:
return str(n) + " " + plural
return tag_it
t_apple = tag_closure("apple", "apples")
t_cherry = tag_closure("cherry", "cherries");
print t_apple(1), "and", t_cherry(15)
这可能比下面的方式更清晰:
class tag_object(object):
def __init__(self, singular, plural):
self.singular = singular
self.plural = plural
def tag(self, n):
if n == 1:
return "1 " + self.singular
else:
return str(n) + " " + self.plural
t_apple = tag_object("apple", "apples")
t_cherry = tag_object("cherry", "cherries");
print t_apple.tag(1), "and", t_cherry.tag(15)
作为一个经验法则:如果这个东西真的只是一个单一的函数,并且只是捕捉静态状态,那么可以考虑使用闭包。如果这个东西是打算有可变状态,或者有多个函数,就用类。
换句话说:如果你在创建一个闭包的字典,实际上就是在手动重复类的机制。最好还是让语言本身提供的结构来处理这个。
在Python中,闭包的调试和使用可能比普通对象要复杂一些(你需要把可调用的函数保存到某个地方,然后用奇怪的方式去访问它们,比如用clos['add']
这种写法等等)。举个例子,如果你发现结果有点奇怪,想要检查sum
的值,那就很麻烦了……调试这种情况真的很困难;-)
唯一的好处就是简单性——但这主要只适用于非常简单的情况(如果你有三个内部函数可调用的话,我觉得就有点过了)。
也许闭包提供的强保护(相比于对象的“约定”保护,比如类和实例对象)或者对JavaScript程序员来说可能更熟悉的用法,可能在某些特殊情况下让人选择使用闭包而不是类和实例,但实际上我并没有遇到过这样的情况——所以我只在非常简单的情况下使用闭包;-)