A többszörös értékek visszaadásának kanonikus módja az ezt támogató nyelvekben gyakran a tupling.
Tekintsük ezt a triviális példát:
def f(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return (y0, y1, y2)
Ez azonban gyorsan problémássá válik, ahogy a visszaadott értékek száma növekszik. Mi van akkor, ha négy vagy öt értéket szeretne visszaadni? Persze, folytathatod a többszörözést, de könnyen elfelejted, hogy melyik érték hol van. Ráadásul elég csúnya dolog kicsomagolni őket oda, ahová kapni akarod őket.
A következő logikus lépésnek tűnik valamilyen 'rekordjegyzetelés' bevezetése. Pythonban ennek kézenfekvő módja a dict
.
Tekintsük a következőt:
def g(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return {'y0': y0, 'y1': y1 ,'y2': y2}
(Csak hogy világos legyen, y0, y1 és y2 csak absztrakt azonosítóként értendő. Mint rámutattunk, a gyakorlatban értelmes azonosítókat használnánk).
Most már van egy olyan mechanizmusunk, amellyel kivetíthetjük a visszaadott objektum egy adott tagját. Például,
result['y0']
Van azonban egy másik lehetőség is. Ehelyett visszaadhatunk egy speciális struktúrát. Ezt a Python kontextusában fogalmaztam meg, de biztos vagyok benne, hogy más nyelvekre is alkalmazható. Sőt, ha C nyelven dolgoznánk, akkor ez lehet az egyetlen lehetőségünk. Itt is van:
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)
Pythonban az előző kettő talán nagyon hasonló a vízvezeték-szerelés szempontjából - elvégre { y0, y1, y2 }
végül is csak a ReturnValue
belső __dict__
bejegyzései.
Van azonban egy további funkció, amit a Python biztosít az apró objektumok számára, a __slots__
attribútum. Az osztály kifejezhető így:
class ReturnValue(object):
__slots__ = ["y0", "y1", "y2"]
def __init__(self, y0, y1, y2):
self.y0 = y0
self.y1 = y1
self.y2 = y2
A Python Reference Manual című kézikönyvből:
A
__slots__
deklaráció egy sor példányváltozót vesz fel, és minden példányban csak annyi helyet foglal el, hogy minden egyes változónak egy értéket tudjon tárolni. A helyet azért takarítjuk meg, mert a__dict__
nem jön létre minden egyes példányhoz.
A Python 3.7'új dataclassok használatával egy olyan osztályt adhatunk vissza, amely automatikusan hozzáadott speciális metódusokkal, tipizálással és egyéb hasznos eszközökkel rendelkezik:
@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)
Egy másik javaslat, amit elnéztem, Bill the Lizardtól származik:
def h(x):
result = [x + 1]
result.append(x * 3)
result.append(y0 ** y3)
return result
Bár ez a legkevésbé kedvenc módszerem. Gondolom, a Haskellnek való kitettségem miatt megfertőződtem, de a vegyes típusú listák ötlete mindig is kényelmetlen volt számomra. Ebben a konkrét példában a lista -nem- vegyes típusú, de elképzelhető, hogy lehetne.
Egy ilyen módon használt lista tényleg nem nyer semmit a tuple-hez képest, amennyire meg tudom ítélni. Az egyetlen valódi különbség a listák és a tuple-ok között Pythonban az, hogy a listák mutable, míg a tuple-ok nem.
Én személy szerint hajlamos vagyok átvenni a funkcionális programozásból származó konvenciókat: listákat használok tetszőleges számú azonos típusú elemre, és tuple-okat előre meghatározott típusú elemek fix számú elemére.
A hosszas bevezető után jön az elkerülhetetlen kérdés. Melyik módszer (szerinted) a legjobb?
Én jellemzően a szótári útvonalat választottam, mert ez kevesebb beállítási munkával jár. A típusok szempontjából azonban lehet, hogy jobban jársz az osztályos útvonalon, mivel így elkerülheted, hogy összekeveredjen, hogy mit képvisel egy szótár.
Másrészt vannak a Python közösségben olyanok, akik úgy gondolják, hogy az explicit interfészekkel szemben előnyben kell részesíteni a hallgatólagos interfészeket, és ekkor az objektum típusa nem igazán releváns, mivel alapvetően arra a konvencióra támaszkodsz, hogy ugyanaz az attribútum mindig ugyanazt a jelentést fogja jelenteni.
Szóval, hogyan adhatsz vissza több értéket Pythonban?
Jobban szeretem:
def g(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return {'y0':y0, 'y1':y1 ,'y2':y2 }
Úgy tűnik, hogy minden más csak extra kód ugyanarra a célra.
Általában a "specializált struktúra" valójában egy objektum értelmes aktuális állapota, saját metódusokkal.
class Some3SpaceThing(object):
def __init__(self,x):
self.g(x)
def g(self,x):
self.y0 = x + 1
self.y1 = x * 3
self.y2 = y0 ** y3
r = Some3SpaceThing( x )
r.y0
r.y1
r.y2
Szeretek neveket találni névtelen struktúráknak, ahol csak lehet. A jelentéses nevek egyértelműbbé teszik a dolgokat.
Az olyan nyelveken, mint a Python, általában szótárat használok, mivel ez kevesebb többletköltséggel jár, mint egy új osztály létrehozása.
Ha azonban úgy találom magam, hogy folyamatosan ugyanazt a változóhalmazt adom vissza, akkor ez valószínűleg egy új osztályt jelent, amelyet én'll faktorálok.