Я могу определить объект и присвоить атрибуты и методы:
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']()
Я думаю, что объектный синтаксис выглядел бы более чистым к большинству средств просмотра, но является там экземплярами, в которых использование закрытия было бы семантически предпочтительно?
В Python замыкания может быть сложнее отлаживать и использовать, чем более обычные объекты (вам нужно где-то сохранять вызываемые объекты, обращаться к ним с тупой нотацией clos ['add' ]
и т. Д., ...). Рассмотрим, например, невозможность доступа к сумме
, если вы обнаружите что-то странное в результате ... отладка такого рода вещей может быть очень сложной; -)
Единственным реальным компенсирующим преимуществом является простота - но в основном это применимо только к действительно простым случаям (к тому времени, когда у вас есть три вызываемых объекта, которые являются внутренними функциями, я бы сказал, что вы за бортом в этом отношении).
Возможно очень сильная защита (по сравнению с защитой объектов «по соглашению» для объектов классов и экземпляров) или возможная более высокая степень осведомленности программистов Javascript могла бы оправдать использование замыканий, а не классов и экземпляров в крайних случаях, но на практике я не попадал в случаи, когда казалось бы, что такие гипотетические преимущества действительно применимы, поэтому я использую замыкания только в действительно простых случаях ;-).
Вы должны использовать ту версию, которая наиболее четко выражает то, чего вы пытаетесь достичь.
В приведенном примере я бы сказал, что версия object более понятна, поскольку, похоже, моделируется объект с изменяющимся состоянием. Если посмотреть на код, который использует значение, то объектная версия, кажется, выражает ясное намерение, в то время как версия закрытия, кажется, имеет операции (индексирование и "волшебные" строки), которые являются второстепенными.
В 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)
В качестве эмпирического правила: если вещь действительно является только одной функцией, и она фиксирует только статическое состояние, то рассмотрите вариант закрытия. Если предполагается, что вещь будет иметь изменяемое состояние, и/или имеет более одной функции, используйте класс.
Другой способ выразить это: Если вы создаете дикту закрытий, вы, по сути, дублируете механизм класса вручную. Лучше доверить это языковой конструкции, предназначенной для этого.
Единственная реальная причина, которую я вижу для использования закрытых объектов - это если вы хотите получить довольно сильную гарантию того, что пользователь вашего объекта/закрытого объекта не имеет доступа к скрытой переменной.
Что-то вроде этого:
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()
Синтаксис вызова класса выглядит приятнее, и вы можете создавать подклассы классов. Закрытие также предполагает повторение всех имен, которые вы хотите раскрыть, в операторе return
. Возможно, это нормально, если вы хотите иметь действительно приватные методы, которые более скрыты, чем обычное соглашение о префиксе подчеркивания