objects or closures - when to use?
I can define an object and assign attributes and methods:
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()
or provide the same functionality by defining a closure:
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']()
I think the object s开发者_JS百科yntax would look cleaner to most viewers, but are there instances in which the use of a closure would be semantically preferable?
You should use the version that most clearly expresses what you are trying to achieve.
In the example given, I'd say that object version is more clear, since it seems to be modeling an object with state that changes. Looking at the code that uses the value, the object version seems to express the clear intent, whereas the closure version seems to have operations (the indexing and 'magic' strings) that are beside the point.
In Python, I would favor a closure based approach when what is needed is something that is mostly like a function, and perhaps needs to capture some state.
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)
This is perhaps a little clearer than the following:
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)
As a rule of thumb: If the thing is really only a single function, and it is only capturing static state, then consider a closure. If the thing is intended to have mutable state, and/or has more than one function, use a class.
Another way to put it: If you are creating a dict of closures, you are essentially duplicating the class machinery by hand. Better to leave it to the language construct designed to do it.
In Python, closures can be harder to debug and to use than the more usual objects (you have to save the callables somewhere, access them with the goofy notation clos['add']
etc, ...). Consider for example the impossibility of accessing the sum
if you find something strange in the result... debugging this kind of thing can be really hard;-)
The only real compensating advantage is simplicity -- but it basically applies only to really simple cases (by the time you have three callables that are internal functions I'd say you're overboard in that respect).
Maybe the very strong protection (vs objects' protection "by convention" for class and instance objects), or the possible higher familiarity to Javascript programmers, might justify using closures rather than classes and instances in marginal cases, but in practice I haven't found myself in cases where such hypothetical advantages seemed to actually apply -- so I only use closures in really simple cases;-).
The only real reason I can see for using closures is, if you want to a rather strong guarantee that the user of your object/closure doesn't have access to a hidden variable.
Something like this:
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()
I made a comment to the effect that using function attributes, a closure can utilize the same syntax as a class because functions are objects in python. All the internal variables also become accessible just like the class method.
I'm curious if this approach does Bad Things that I'm not aware of.
def closure(a, b):
def add():
closure.sum = a + b
def subtr():
closure.fin = closure.sum - b
def getpar():
return closure.fin
closure.add = add;
closure.subtr = subtr
closure.getpar = getpar
return closure
clo = closure(2,3)
clo.add()
clo.subtr()
print(clo.getpar())
print(clo.sum)
print(clo.fin)
The calling syntax for the class looks nicer and you can subclass classes. The closure also involves repeating all names you wish to expose in the return
statement. Perhaps that is ok though if you want to have really private methods that are more hidden than the usual underscore prefix convention
精彩评论