对象还是闭包 - 何时使用?

16 投票
5 回答
4238 浏览
提问于 2025-04-16 00:51

我可以定义一个对象,并给它添加属性和方法:

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 个回答

3

我觉得使用闭包的唯一真正原因就是,如果你想要一个相当强的保证,确保使用你对象或闭包的人无法访问到一个隐藏的变量。

比如说:

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()
19

你应该选择最能清楚表达你想要实现的版本。

在这个例子中,我觉得用对象的方式更清晰,因为它看起来是在模拟一个状态会变化的对象。看使用这个值的代码时,对象的版本似乎更能表达出明确的意图,而闭包的版本则有一些操作(比如索引和一些“魔法”字符串)显得有些多余。

在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)

作为一个经验法则:如果这个东西真的只是一个单一的函数,并且只是捕捉静态状态,那么可以考虑使用闭包。如果这个东西是打算有可变状态,或者有多个函数,就用类。

换句话说:如果你在创建一个闭包的字典,实际上就是在手动重复类的机制。最好还是让语言本身提供的结构来处理这个。

9

在Python中,闭包的调试和使用可能比普通对象要复杂一些(你需要把可调用的函数保存到某个地方,然后用奇怪的方式去访问它们,比如用clos['add']这种写法等等)。举个例子,如果你发现结果有点奇怪,想要检查sum的值,那就很麻烦了……调试这种情况真的很困难;-)

唯一的好处就是简单性——但这主要只适用于非常简单的情况(如果你有三个内部函数可调用的话,我觉得就有点过了)。

也许闭包提供的强保护(相比于对象的“约定”保护,比如类和实例对象)或者对JavaScript程序员来说可能更熟悉的用法,可能在某些特殊情况下让人选择使用闭包而不是类和实例,但实际上我并没有遇到过这样的情况——所以我只在非常简单的情况下使用闭包;-)

撰写回答