google Wie gibst du mehrere Werte in Python zurück?




python docstring (11)

Die kanonische Methode, mehrere Werte in Sprachen zurückzugeben, die sie unterstützen, ist häufig tupling .

Option: Verwenden eines Tupels

Betrachten Sie dieses triviale Beispiel:

def f(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return (y0,y1,y2)

Dies wird jedoch schnell problematisch, da die Anzahl der zurückgegebenen Werte zunimmt. Was, wenn Sie vier oder fünf Werte zurückgeben möchten? Sicher, Sie könnten sie weiter tupfen, aber es wird leicht zu vergessen, welcher Wert wo ist. Es ist auch ziemlich hässlich, sie auszupacken, wo auch immer Sie sie erhalten möchten.

Option: Verwenden eines Wörterbuchs

Der nächste logische Schritt scheint zu sein, eine Art "Notation" einzuführen. In Python ist dies offensichtlich durch ein dict .

Folgendes berücksichtigen:

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0':y0, 'y1':y1 ,'y2':y2 }

(Bearbeiten- Nur um klar zu sein, y0, y1 und y2 sind nur als abstrakte Bezeichner gemeint. Wie bereits erwähnt, würden Sie in der Praxis sinnvolle Bezeichner verwenden)

Jetzt haben wir einen Mechanismus, mit dem wir ein bestimmtes Mitglied des zurückgegebenen Objekts projizieren können. Beispielsweise,

result['y0']

Option: Verwenden einer Klasse

Es gibt jedoch noch eine andere Möglichkeit. Wir könnten stattdessen eine spezialisierte Struktur zurückgeben. Ich habe das im Zusammenhang mit Python dargestellt, aber ich bin mir sicher, dass es auch für andere Sprachen gilt. In der Tat, wenn Sie in C arbeiten, könnte dies Ihre einzige Option sein. Hier geht:

class ReturnValue(object):
  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 sind die vorherigen zwei vielleicht in Bezug auf die Klempnerarbeit sehr ähnlich. Schließlich sind { y0, y1, y2 } nur Einträge im internen __dict__ des ReturnValue .

Es gibt jedoch ein zusätzliches Feature, das von Python für winzige Objekte zur Verfügung gestellt wird: das Attribut __slots__ . Die Klasse könnte ausgedrückt werden als:

class ReturnValue(object):
  __slots__ = ["y0", "y1", "y2"]
  def __init__(self, y0, y1, y2):
     self.y0 = y0
     self.y1 = y1
     self.y2 = y2

Aus dem Python-Referenzhandbuch :

Die __slots__ Deklaration nimmt eine Sequenz von Instanzvariablen und reserviert gerade genug Speicherplatz in jeder Instanz, um einen Wert für jede Variable zu __slots__ . __dict__ gespeichert, da __dict__ nicht für jede Instanz erstellt wird.

Option: Verwenden einer Liste

Ein weiterer Vorschlag, den ich übersehen hatte, stammt von Bill the Lizard:

def h(x):
  result = [x + 1]
  result.append(x * 3)
  result.append(y0 ** y3)
  return result

Dies ist jedoch meine am wenigsten bevorzugte Methode. Ich nehme an, ich bin von Haskell betroffen, aber die Idee gemischter Listen war mir immer unangenehm. In diesem speziellen Beispiel ist die Liste kein gemischter Typ, könnte aber denkbar sein. Eine Liste, die auf diese Art und Weise verwendet wird, gewinnt wirklich nichts in Bezug auf das Tupel, soweit ich das beurteilen kann. Der einzige wirkliche Unterschied zwischen Listen und Tupeln in Python besteht darin, dass Listen änderbar sind, Tupel dagegen nicht. Ich persönlich tendiere dazu, die Konventionen aus der funktionalen Programmierung zu übernehmen: benutze Listen für eine beliebige Anzahl von Elementen des gleichen Typs und Tupel für eine feste Anzahl von Elementen vorbestimmter Typen.

Frage

Nach der langen Präambel kommt die unvermeidliche Frage. Welche Methode (meinst du) ist am besten?

In der Regel habe ich die Wörterbuchroute gefunden, weil es weniger Setup-Arbeit erfordert. Aus Sicht der Typen kann es jedoch besser sein, die Klassenroute zu verwenden, da Sie dadurch möglicherweise nicht verwirren können, was ein Wörterbuch darstellt. Auf der anderen Seite gibt es in der Python-Community einige implizite Interfaces, die gegenüber expliziten Interfaces bevorzugt sind. An diesem Punkt ist der Typ des Objekts wirklich nicht relevant, da Sie sich grundsätzlich auf die Konvention verlassen, dass dasselbe Attribut verwendet wird wird immer dieselbe Bedeutung haben.

Wie können Sie also in Python mehrere Werte zurückgeben?


Answer #1

Bei kleinen Projekten finde ich es am einfachsten mit Tupeln zu arbeiten. Wenn das zu schwierig wird (und nicht vorher), fange ich an, Dinge in logische Strukturen zu gruppieren, aber ich denke, Ihre vorgeschlagene Verwendung von Wörterbüchern und ReturnValue-Objekten ist falsch (oder zu einfach).

Das Zurückgeben eines Wörterbuchs mit den Tasten y0, y1, y2 usw. bietet keinen Vorteil gegenüber Tupeln. Die Rückgabe einer ReturnValue-Instanz mit den Eigenschaften .y0 .y1 .y2 usw. bietet auch keinen Vorteil gegenüber Tupeln. Sie müssen anfangen, Dinge zu benennen, wenn Sie irgendwo hinkommen wollen, und Sie können das trotzdem mit Tupeln tun:

def getImageData(filename):
  [snip]
  return size, (format, version, compression), (width,height)
size, type, dimensions = getImageData(x)

IMHO, die einzige gute Technik jenseits von Tupeln ist, echte Objekte mit geeigneten Methoden und Eigenschaften zurückzugeben, wie Sie es von re.match() oder open(file) .


Answer #2

Eine andere Option wäre die Verwendung von Generatoren:

>>> def f(x):
        y0 = x + 1
        yield y0
        yield x * 3
        yield y0 ** 4


>>> a, b, c = f(5)
>>> a
6
>>> b
15
>>> c
1296

Obwohl IMHO-Tupel normalerweise am besten sind, außer in Fällen, in denen die zurückgegebenen Werte Kandidaten für die Kapselung in einer Klasse sind.


Answer #3

Benannte Tupel wurden zu diesem Zweck in 2.6 hinzugefügt. Siehe auch os.stat für ein ähnliches eingebautes Beispiel.

>>> import collections
>>> Point = collections.namedtuple('Point', ['x', 'y'])
>>> p = Point(1, y=2)
>>> p.x, p.y
1 2
>>> p[0], p[1]
1 2

Answer #4

+1 auf S.Lotts Vorschlag einer benannten Container-Klasse.

Für python 2.6 und höher bietet ein benanntes Tupel eine nützliche Möglichkeit zum einfachen Erstellen dieser Container-Klassen, und die Ergebnisse sind "leichtgewichtig und benötigen keinen Speicher mehr als normale Tupel".


Answer #5

Pythons Tupel, Dicts und Objekte bieten dem Programmierer einen reibungslosen Kompromiss zwischen Formalität und Zweckmäßigkeit für kleine Datenstrukturen ("Dinge"). Für mich ist die Entscheidung, wie man etwas repräsentiert, hauptsächlich davon abhängig, wie ich die Struktur nutzen werde. In C ++ ist es eine gängige Konvention, für Daten nur Objekte und class für Objekte mit Methoden zu verwenden, obwohl Sie Methoden legal auf eine struct . Meine Gewohnheit ist in Python ähnlich, mit dict und tuple anstelle von struct .

Bei Koordinatensätzen verwende ich eher ein tuple als eine dict oder ein dict (und beachten Sie, dass Sie ein tuple als Dictionary-Schlüssel verwenden können, so dass dict 's große, dünne mehrdimensionale Arrays erzeugen).

Wenn ich über eine Liste von Dingen iteriere, entpacke ich lieber tuple s auf der Iteration:

for score,id,name in scoreAllTheThings():
    if score > goodScoreThreshold:
        print "%6.3f #%6d %s"%(score,id,name)

... da die Objektversion mehr unübersichtlich zu lesen ist:

for entry in scoreAllTheThings():
    if entry.score > goodScoreThreshold:
        print "%6.3f #%6d %s"%(entry.score,entry.id,entry.name)

... geschweige denn das dict .

for entry in scoreAllTheThings():
    if entry['score'] > goodScoreThreshold:
        print "%6.3f #%6d %s"%(entry['score'],entry['id'],entry['name'])

Wenn das Ding weit verbreitet ist und Sie ähnliche nicht-triviale Operationen an mehreren Stellen im Code ausführen, ist es normalerweise sinnvoll, es mit geeigneten Methoden zu einem Klassenobjekt zu machen.

Schließlich, wenn ich Daten mit Nicht-Python-Systemkomponenten austauschen werde, werde ich sie am häufigsten in einem dict da dies für die JSON-Serialisierung am besten geeignet ist.


Answer #6

Viele der Antworten legen nahe, dass Sie eine Sammlung einer Art, wie ein Wörterbuch oder eine Liste, zurückgeben müssen. Sie könnten die zusätzliche Syntax weglassen und einfach die Rückgabewerte komma-separiert ausgeben. Hinweis: Dies gibt technisch ein Tupel zurück.

def f():
    return True, False
x, y = f()
print(x)
print(y)

gibt:

True
False

Answer #7

Ich würde ein Diktat verwenden, um Werte von einer Funktion zu übergeben und zurückzugeben:

Verwenden Sie das im form definierte variable form .

form = {
    'level': 0,
    'points': 0,
    'game': {
        'name': ''
    }
}


def test(form):
    form['game']['name'] = 'My game!'
    form['level'] = 2

    return form

>>> print(test(form))
{u'game': {u'name': u'My game!'}, u'points': 0, u'level': 2}

Dies ist der effizienteste Weg für mich und für die Verarbeitungseinheit.

Sie müssen nur einen Zeiger eingeben und nur einen Zeiger zurückgeben.

Sie müssen die Argumente der Funktionen (Tausende von ihnen) nicht ändern, wenn Sie Ihren Code ändern.


Answer #8

Im Allgemeinen ist die "spezialisierte Struktur" tatsächlich ein vernünftiger aktueller Zustand eines Objekts mit eigenen Methoden.

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

Ich suche möglichst nach Namen für anonyme Strukturen. Sinnvolle Namen machen die Dinge klarer.


Answer #9

ich bevorzuge

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0':y0, 'y1':y1 ,'y2':y2 }

Es scheint, alles andere ist nur zusätzlicher Code, um das Gleiche zu tun.


Answer #10

In Sprachen wie Python würde ich normalerweise ein Wörterbuch verwenden, da es weniger Aufwand erfordert als das Erstellen einer neuen Klasse.

Wenn ich jedoch immer wieder denselben Satz von Variablen zurückgebe, dann handelt es sich wahrscheinlich um eine neue Klasse, die ich ausklammern werde.


Answer #11

Ich bevorzuge Tupel, wenn ein Tupel sich "natürlich" fühlt; Koordinaten sind ein typisches Beispiel, bei dem die einzelnen Objekte eigenständig stehen können, z. B. bei einachsigen Skalierungsberechnungen.

Ich verwende Wörterbücher nur dann als Rückgabewert, wenn die gruppierten Objekte nicht immer gleich sind. Denken Sie an optionale E-Mail-Header.

Für den Rest der Fälle, wo die gruppierten Objekte innerhalb der Gruppe eine inhärente Bedeutung haben oder ein vollwertiges Objekt mit eigenen Methoden benötigt wird, verwende ich eine Klasse.





return-value