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 definedpo prostu użycie3?