2010-07-05 14 views
13

posso definire un oggetto e assegnare attributi e metodi:oggetti o chiusure - quando usare?

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

o fornire la stessa funzionalità definendo una chiusura:

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

ritengo la sintassi dell'oggetto apparirebbe detergente alla maggior parte dei telespettatori, ma ci sono casi in cui l'uso di una chiusura sarebbe semanticamente preferibile?

+1

Vedere anche http://stackoverflow.com/questions/256625/when-to-use-closure/256651#256651. ;) – FMc

+1

:) Sì, Stephen dovrebbe studiare sotto Qc Na. – Yuji

+0

Sta ancora prendendo apprendisti? :) – hatmatrix

risposta

7

In Python, le chiusure possono essere più difficili da eseguire per il debug e da utilizzare rispetto agli oggetti più comuni (è necessario salvare le callebles da qualche parte, accedervi con la notazione goofy clos['add'] ecc.). Prendi ad esempio l'impossibilità di accedere allo sum se trovi qualcosa di strano nel risultato ... il debugging di questo tipo di cose può essere davvero difficile ;-)

L'unico vero vantaggio di compensazione è la semplicità - ma in pratica si applica solo a casi davvero semplici (nel momento in cui hai tre callables che sono funzioni interne direi che sei fuori bordo a tale riguardo).

Forse la protezione molto forte (vs protezione oggetti 'per convenzione' per gli oggetti di classe e istanza), o l'eventuale maggiore familiarità programmatori JavaScript, potrebbe giustificare l'uso chiusure piuttosto che le classi e le istanze in casi marginali, ma in pratica non mi sono trovato nei casi in cui tali vantaggi ipotetici sembrassero effettivamente applicabili - quindi uso solo chiusure in casi davvero semplici ;-).

+0

true sul miglior supporto per il debug per le classi ... – hatmatrix

+0

Se si utilizzano gli attributi di funzione anziché un dizionario, la chiusura ha lo stesso utilizzo della classe. Scriverò una risposta qui sotto. – JDG

1

La sintassi di chiamata per la classe è più gradevole ed è possibile sottoclasse classi. La chiusura comporta anche la ripetizione di tutti i nomi che si desidera esporre nell'istruzione return. Forse è ok se vuoi avere metodi veramente privati ​​che sono più nascosti della solita convenzione prefisso underscore

+0

sì, non sottoclassi spesso ma questo è un argomento forte per gli oggetti – hatmatrix

+0

Sono curioso di sapere cosa ne pensi del mio commento qui sotto dove gli attributi di funzione sono usati per creare la stessa sintassi che si otterrebbe con un oggetto più la convenienza aggiunta di non (facilmente) esponendo le funzionalità interne a meno che non lo includiate esplicitamente nell'istruzione return. – JDG

15

Dovresti usare la versione che più chiaramente esprime ciò che stai cercando di ottenere.

Nell'esempio fornito, direi che la versione dell'oggetto è più chiara, poiché sembra che si stia modellando un oggetto con stato che cambia. Osservando il codice che usa il valore, la versione dell'oggetto sembra esprimere il chiaro intento, mentre la versione di chiusura sembra avere delle operazioni (l'indicizzazione e le stringhe 'magiche') che sono al di là del punto.

In Python, vorrei favorire un approccio basato sulla chiusura quando ciò che è necessario è qualcosa che è principalmente come una funzione, e forse ha bisogno di catturare alcuni stati.

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) 

Questo è forse un po 'più chiaro che il seguente:

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) 

Come regola generale: se la cosa è in realtà solo una singola funzione, e sta catturando solo condizione statica, quindi prendere in considerazione una chiusura. Se la cosa è destinata ad avere uno stato mutabile e/o ha più di una funzione, usa una classe.

Un altro modo per dirlo: se si sta creando un ditt di chiusure, si sta essenzialmente duplicando la macchina di classe a mano. Meglio lasciarlo al costrutto linguistico progettato per farlo.

+2

+1 per enfatizzare la leggibilità/chiarezza del codice come punto di decisione. – spade78

3

L'unica vera ragione che posso vedere per l'utilizzo delle chiusure è, se si desidera una garanzia piuttosto forte, che l'utente del proprio oggetto/chiusura non abbia accesso a una variabile nascosta.

Qualcosa di simile a questo:

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

Non sono sicuro che possiamo fare affidamento sulle chiusure per rendere le variabili private. Uno potrebbe retrofit 's = Shotgun()' con 's.shoot.func_closure [0] .cell_contents ['ammo'] = 1e20' – unutbu

+0

Hai ragione. Non è davvero una buona idea affidarsi a questo per sicurezza. – phoku

+2

Ben scavare in una chiusura sembra meno semplice dell'accesso agli interni di un oggetto di classe usando almeno la notazione "obj.variable =". – spade78

1

ho fatto un commento, secondo la quale l'uso della funzione attributi, una chiusura possono utilizzare la stessa sintassi come una classe perché le funzioni sono oggetti in Python. Tutte le variabili interne diventano accessibili come il metodo di classe.

Sono curioso di sapere se questo approccio fa cose cattive di cui non sono a conoscenza.

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) 
Problemi correlati