Il modo canonico di restituire più valori nelle lingue che lo supportano è spesso tupling .
Opzione: usare una tupla
Considera questo banale esempio:
def f(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return (y0, y1, y2)
Tuttavia, questo diventa rapidamente problematico all'aumentare del numero di valori restituiti. Cosa succede se si desidera restituire quattro o cinque valori? Certo, potresti continuare a agitarli, ma diventa facile dimenticare quale valore è dove. È anche piuttosto brutto disimballarli ovunque tu voglia riceverli.
Opzione: utilizzo di un dizionario
Il prossimo passo logico sembra essere quello di introdurre una sorta di "notazione record". In Python, il modo più ovvio per farlo è tramite a dict
.
Considera quanto segue:
def g(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return {'y0': y0, 'y1': y1 ,'y2': y2}
(Giusto per essere chiari, y0, y1 e y2 sono intesi solo come identificatori astratti. Come sottolineato, in pratica useresti identificatori significativi.)
Ora, abbiamo un meccanismo per cui possiamo proiettare un particolare membro dell'oggetto restituito. Per esempio,
result['y0']
Opzione: utilizzo di una classe
Tuttavia, c'è un'altra opzione. Potremmo invece restituire una struttura specializzata. L'ho inquadrato nel contesto di Python, ma sono sicuro che si applica anche ad altre lingue. In effetti, se lavorassi in C, questa potrebbe benissimo essere la tua unica opzione. Ecco qui:
class ReturnValue:
def __init__(self, y0, y1, y2):
self.y0 = y0
self.y1 = y1
self.y2 = y2
def g(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return ReturnValue(y0, y1, y2)
In Python i due precedenti sono forse molto simili in termini di impianto idraulico - dopo tutto { y0, y1, y2 }
finiscono per essere voci all'interno __dict__
del ReturnValue
.
C'è una funzionalità aggiuntiva fornita da Python anche se per piccoli oggetti, l' __slots__
attributo. La classe potrebbe essere espressa come:
class ReturnValue(object):
__slots__ = ["y0", "y1", "y2"]
def __init__(self, y0, y1, y2):
self.y0 = y0
self.y1 = y1
self.y2 = y2
Dal manuale di riferimento di Python :
La
__slots__
dichiarazione accetta una sequenza di variabili di istanza e riserva uno spazio sufficiente in ciascuna istanza per contenere un valore per ogni variabile. Lo spazio viene salvato perché__dict__
non viene creato per ogni istanza.
Opzione: utilizzo di una dataclasse (Python 3.7+)
Utilizzando i nuovi dataclass di Python 3.7, restituisci una classe con metodi speciali aggiunti automaticamente, digitazione e altri strumenti utili:
@dataclass
class Returnvalue:
y0: int
y1: float
y3: int
def total_cost(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return ReturnValue(y0, y1, y2)
Opzione: utilizzare un elenco
Un altro suggerimento che avevo trascurato viene da Bill the Lizard:
def h(x):
result = [x + 1]
result.append(x * 3)
result.append(y0 ** y3)
return result
Questo è il mio metodo meno preferito però. Suppongo di essere contaminato dall'esposizione a Haskell, ma l'idea di elenchi di tipo misto mi è sempre sembrata a disagio. In questo esempio particolare l'elenco è di tipo non misto, ma è possibile che lo sia.
Un elenco usato in questo modo in realtà non guadagna nulla rispetto alla tupla, per quanto posso dire. L'unica vera differenza tra liste e tuple in Python è che le liste sono mutabili , mentre le tuple no.
Personalmente tendo a riproporre le convenzioni dalla programmazione funzionale: uso liste per qualsiasi numero di elementi dello stesso tipo e tuple per un numero fisso di elementi di tipi predeterminati.
Domanda
Dopo il lungo preambolo, arriva l'inevitabile domanda. Quale metodo (pensi) sia il migliore?
y3
, che farà lo stesso lavoro.
y3
, ma se y3 non viene dichiarato globale, questo potrebbeNameError: global name 'y3' is not defined
forse usare3
?