Quindi sono curioso di vedere le visualizzazioni di programmatori Python più esperti sulla seguente domanda di stile. Supponiamo che io stia costruendo una funzione che sta andando a scorrere una riga dopo l'altra attraverso un dataframe pandas o un altro caso simile in cui una funzione richiede l'accesso al suo stato precedente. Sembra che ci siano almeno quattro modi per implementare questa in python:Idiomi in python: chiusura vs functor vs oggetto
1) Chiusure:
def outer():
previous_state = None
def inner(current_state) :
nonlocal previous_state
#do something
previous_state=current_state
return something
Quindi, se si proviene da un background javascript questo sarà senza dubbio sembrare naturale per te. Sembra davvero naturale anche in python, fino a quando non avrai bisogno di accedere allo scope che racchiude, quando finirai per fare qualcosa come inner.__code__.co_freevars
, che ti darà i nomi delle variabili che racchiudono come una tupla, e trovare l'indice di quello vuoi, e poi andare a inner.__closure__[index].cell_contents
per ottenere il suo valore. Non proprio elegante, ma suppongo che il punto sia spesso nascondere l'ambito, quindi ha senso che sia difficile da raggiungere. D'altra parte, è anche un po 'strano che Python renda privata la funzione di inclusione quando ha eliminato quasi ogni altro modo di avere una variabile privata rispetto alle lingue OOP.
2) Functor
def outer():
def inner(current_state):
#do something
inner.previous_state=current_state
return something
ret = inner
ret.previous_state=None
return ret
Questa "apre la chiusura" a che ora lo stato racchiude è completamente visibile come attributo della funzione. Funziona perché le funzioni sono in realtà solo oggetti nascosti. Mi sto appoggiando a questo come il più pignolo. È chiaro, conciso e leggibile.
3) Oggetti Questo è probabilmente il più familiare OOP programmatori
class Calculator(Object) :
def __init__(self):
self.previous_state=None
def do_something(self, current_state) :
#do_something
self.previous_state = current_state
return something
Il più grande con qui è che si tende a finire con un sacco di definizioni di classi. Ciò va bene in un linguaggio OOP completo come Java, in cui si hanno interfacce e simili per gestirlo, ma sembra un po 'strano in un linguaggio a forma di deambulatore avere molte semplici classi solo per portare in giro una funzione che ha bisogno di un po' di stato.
4) globali - non voglio dimostrare questo, come ho espressamente voglio evitare di inquinare lo spazio dei nomi globale
5) Decoratori - questo è un po 'una palla curva, ma è possibile utilizzare decoratori per memorizzare parziale informazioni di stato.
@outer
def inner(previous_state, current_state):
#do something
return something
def outer(inner) :
def wrapper(current_state) :
result = inner(wrapper.previous_state, current_state)
wrapper.previous_state = current_state
return result
ret = wrapper
ret.previous_state=None
return result
Questo tipo di sintassi è la meno familiare per me, ma se ora mi chiamo
func = inner
ho effettivamente ottenere
func = outer(inner)
e poi ripetutamente chiamando func()
atti, proprio come il funtore esempio. In realtà odio in questo modo di più. Mi sembra di avere una sintassi davvero non trasparente in quanto non è chiaro se chiamare internamente (current_state) un sacco di volte ti darà lo stesso risultato o se ti darà una funzione appena decorata ogni volta, quindi sembra una cattiva pratica per fare decoratori che aggiungono lo stato a una funzione in questo modo.
Quindi qual è il modo corretto? Che pro e contro ho perso qui?
Non sto seguendo il motivo per cui pensi di aver bisogno di accedere alle variabili chiuse tramite 'inner .__ closure__'; i nomi terminano solo in quella struttura perché li stai utilizzando attivamente nella funzione interna * già *. La struttura '__closure__' è un dettaglio di implementazione interno, davvero. –
Farò in modo che le persone lo sviluppino, ma per esperienza, gli sviluppatori di Python tendono a usare la classe per questo tipo di scenario. O decoratori quando non hai bisogno di piena chiarezza e preferisci la bella sintassi con '@'. Non vuoi usare la parola chiave 'nonlocal' o' global'. E il functor è spesso lasciato appart per la chiusura. – Cyrbil
I nomi di chiusure non sono diversi da quelli locali a questo riguardo; di solito non entri in una funzione per leggere la gente del posto, perché stai facendo la stessa cosa con le chiusure? Se è necessario esporre tale stato per l'uso al di fuori della funzione, utilizzare l'approccio functor o class. –