2010-07-05 5 views
13

я могу определить объект и присвоить атрибуты и методы:объекты или затворы - когда использовать?

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']() 

Я думаю, что синтаксис объекта будет выглядеть чище для большинства зрителей, но существуют ли случаи, когда использование закрытия было бы семантически предпочтительным?

+1

Также см. Http://stackoverflow.com/questions/256625/when-to-use-closure/256651#256651. ;) – FMc

+1

:) Да, Стивен должен учиться под Qc Na. – Yuji

+0

Он все еще принимает учеников? :) – hatmatrix

ответ

7

В Python закрытие может быть сложнее отлаживать и использовать, чем более обычные объекты (вы должны где-то сохранить вызовы, получить к ним доступ с загнутыми нотами clos['add'] и т. Д.). Рассмотрим, например, невозможность доступа к sum, если вы найдете что-то странное в результате ... отладка такого рода вещей может быть очень сложной ;-)

Единственное реальное компенсирующее преимущество - простота, но в принципе оно применяется только к действительно простым случаям (к тому времени, когда у вас есть три вызова, которые являются внутренними функциями, я бы сказал, что вы за борт в этом отношении).

Может очень сильная защита (против защиты объектов по соглашению "для объектов класса и экземпляра), или возможно более близкое знакомство с Javascript программистов, может оправдать использование затворов, а не классы и экземпляры в маргинальных случаях, но на практике я не обнаружил себя в тех случаях, когда такие гипотетические преимущества, по-видимому, действительно применялись - поэтому я использую только закрытие в действительно простых случаях ;-).

+0

истинно о лучшей поддержке отладки для классов ... – hatmatrix

+0

Если вы используете атрибуты функций вместо словаря, то закрытие имеет такое же значение, что и класс. Я напишу ответ ниже. – JDG

1

Вызывающий синтаксис для класса выглядит лучше, и вы можете создавать классы подкласса. Закрытие также включает повторение всех имен, которые вы хотите выставить в инструкции return. Возможно, это нормально, хотя если вы хотите иметь действительно закрытые методы, которые более скрыты, чем обычный текст префикса подчеркивания

+0

Да, я часто не подклассы, но это сильный аргумент для объектов. – hatmatrix

+0

Мне любопытно, что вы думаете о моем комментарии ниже, где атрибуты функции используются для создания того же синтаксиса, который можно получить с объектом, плюс дополнительное удобство не (легко) подвергать внутренности функции, если вы явно не включите ее в оператор return. – JDG

15

Вы должны использовать версию, которая наиболее четко выражает то, чего вы пытаетесь достичь.

В приведенном примере я бы сказал, что объектная версия более понятна, поскольку она, похоже, моделирует объект с измененным состоянием. Рассматривая код, который использует это значение, версия объекта, похоже, выражает ясное намерение, тогда как в версии закрытия есть операции (индексирующие и «магические» строки), которые находятся рядом с точкой.

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

Как правило: если вещь действительно только одна функция, и она только захватывая статическое состояние, а затем рассмотреть закрытие. Если предмет предназначен для изменяемого состояния и/или имеет более одной функции, используйте класс.

Другой способ выразить это: если вы создаете диктовку замыканий, вы по существу дублируете ручную машину класса. Лучше оставить его в построенной языковой конструкции, предназначенной для этого.

+2

+1 для подчеркивания читаемости/четкости кода в качестве точки принятия решения. – spade78

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() 
+3

Я не уверен, что мы можем полагаться на закрытие как способ сделать переменные частными. Можно было бы модифицировать 's = Shotgun()' с 's.shoot.func_closure [0] .cell_contents ['ammo'] = 1e20' – unutbu

+0

Вы правы. На самом деле не стоит полагаться на это для любой безопасности. – phoku

+2

Ну, копание в закрытии кажется менее простым, чем доступ к внутренним объектам объекта класса, используя хотя бы 'obj.variable =' нотацию. – spade78

1

Я сделал замечание о том, что с помощью функции атрибутов, замыкание может использовать тот же синтаксис, как класс, потому что функции являются объектами в питона. Все внутренние переменные также становятся доступными, как метод класса.

Мне любопытно, если этот подход делает плохие вещи, о которых я не знаю.

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) 
Смежные вопросы