Kanonicznym sposobem zwracania wielu wartości w językach, które go obsługują, jest często krotki .
Opcja: użycie krotki
Rozważ ten trywialny przykład:
def f(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return (y0, y1, y2)
Szybko staje się to jednak problematyczne w miarę wzrostu liczby zwracanych wartości. Co jeśli chcesz zwrócić cztery lub pięć wartości? Jasne, możesz je ciągnąć, ale łatwo zapomnieć, która wartość jest gdzie. Rozpakowywanie ich w dowolnym miejscu, w którym chcesz je otrzymać, jest raczej brzydkie.
Opcja: Korzystanie ze słownika
Kolejnym logicznym krokiem wydaje się wprowadzenie pewnego rodzaju „zapisu zapisu”. W Pythonie oczywistym sposobem na to jest użycie dict
.
Rozważ następujące:
def g(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return {'y0': y0, 'y1': y1 ,'y2': y2}
(Żeby być jasnym, y0, y1 i y2 są po prostu abstrakcyjnymi identyfikatorami. Jak wskazano, w praktyce używałbyś znaczących identyfikatorów).
Teraz mamy mechanizm, dzięki któremu możemy rzutować określonego członka zwracanego obiektu. Na przykład,
result['y0']
Opcja: Korzystanie z klasy
Istnieje jednak inna opcja. Zamiast tego moglibyśmy zwrócić specjalistyczną strukturę. Zrobiłem to w kontekście Pythona, ale jestem pewien, że dotyczy to również innych języków. Rzeczywiście, jeśli pracujesz w C, może to być Twoja jedyna opcja. Tutaj idzie:
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)
W Pythonie poprzednie dwa są prawdopodobnie bardzo podobny pod względem kanalizacji - w końcu { y0, y1, y2 }
po prostu skończyć się wpisy w wewnętrznym __dict__
z ReturnValue
.
Istnieje jednak jedna dodatkowa funkcja zapewniana przez Python dla małych obiektów, __slots__
atrybut. Klasa może być wyrażona jako:
class ReturnValue(object):
__slots__ = ["y0", "y1", "y2"]
def __init__(self, y0, y1, y2):
self.y0 = y0
self.y1 = y1
self.y2 = y2
Z podręcznika Python Reference Manual :
__slots__
Deklaracja wykonuje sekwencję zmiennych instancji i rezerwy tylko wystarczająco dużo miejsca w każdym przypadku do przechowywania wartości dla każdej zmiennej. Miejsce jest oszczędzane, ponieważ__dict__
nie jest tworzone dla każdej instancji.
Opcja: Korzystanie z klasy danych (Python 3.7+)
Korzystając z nowych klas danych Python 3.7, zwróć klasę z automatycznie dodanymi specjalnymi metodami, pisaniem i innymi przydatnymi narzędziami:
@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)
Opcja: za pomocą listy
Kolejna sugestia, którą przeoczyłem, pochodzi od Billa Jaszczurki:
def h(x):
result = [x + 1]
result.append(x * 3)
result.append(y0 ** y3)
return result
Jest to jednak moja najmniej ulubiona metoda. Przypuszczam, że jestem skażony kontaktem z Haskellem, ale pomysł list mieszanych zawsze był dla mnie niewygodny. W tym konkretnym przykładzie lista nie jest mieszana, ale możliwe, że tak.
Tak użyta lista tak naprawdę nic nie zyskuje w stosunku do krotki. Jedyną prawdziwą różnicą między listami a krotkami w Pythonie jest to, że listy są zmienne , a krotki nie.
Osobiście mam tendencję do przenoszenia konwencji z programowania funkcjonalnego: używam list dla dowolnej liczby elementów tego samego typu, a krotek dla stałej liczby elementów z góry określonych typów.
Pytanie
Po długiej preambule pojawia się nieuniknione pytanie. Która metoda (według Ciebie) jest najlepsza?
y3
, która również wykona tę samą pracę.
y3
, ale jeśli y3 nie zostanie zadeklarowane jako globalne, może to daćNameError: global name 'y3' is not defined
po prostu użycie3
?