Wie gebe ich mehrere Werte von einer Funktion?
Der übliche Weg, um mehrere Werte zurückgeben, die in Sprachen, die es unterstützen, ist oft tupling.
Option: Mit einem Tupel
Betrachten Sie dieses einfache Beispiel:
def f(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return (y0, y1, y2)
Jedoch, das wird schnell problematisch wird, da die Anzahl der zurückgegebenen Werte erhöht. Was, wenn Sie zurückkehren möchten, vier oder fünf Werte? Sicher, man könnte immer tupling, aber es wird leicht vergessen, welcher Wert wo. Es ist auch eher hässlich entpacken Sie Sie, wohin Sie wollen, Sie zu empfangen.
Option: eine Wörterbuch -
Der nächste logische Schritt zu sein scheint, die Einführung einer Art "record-notation'. In Python, die offensichtliche Weg, dies zu tun ist durch eine dict
.
Folgendes:
def g(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return {'y0': y0, 'y1': y1 ,'y2': y2}
(Nur um klar zu sein, y0, y1 und y2 dienen nur als abstrakte Bezeichner. Wie hingewiesen, in der Praxis würden Sie verwenden Sie sinnvolle Bezeichner.)
Nun, wir haben einen Mechanismus, durch den wir Projekt aus einem bestimmten Mitglied des zurückgegebenen Objekts. Zum Beispiel,
result['y0']
Option: Verwendung einer Klasse
Allerdings gibt es eine weitere option. Wir könnten stattdessen Rückkehr eine spezialisierte Struktur. Ich habe gerahmt, diese in den Kontext von Python, aber ich bin sicher, es gilt auch für andere Sprachen. In der Tat, wenn Sie arbeiten wurden in C das könnte sehr gut sein, Ihre einzige option. Hier geht:
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 die beiden vorherigen sind vielleicht sehr ähnlich in Bezug auf Sanitär -, nachdem alle { y0, y1, y2 }
nur am Ende als Einträge im internen __dict__
des ReturnValue
.
Es ist eine zusätzliche Funktion, bereitgestellt durch Python-obwohl für winzige Objekte, die __slots__
Attribut. Die Klasse könnte wie folgt ausgedrückt werden:
class ReturnValue(object):
__slots__ = ["y0", "y1", "y2"]
def __init__(self, y0, y1, y2):
self.y0 = y0
self.y1 = y1
self.y2 = y2
Aus der Python-Referenz-Handbuch:
Den
__slots__
Erklärung nimmt, eine Sequenz von Instanz-Variablen und behält sich nur genug Platz in jede Instanz einen Wert für jede variable. Speicherplatz wird gespart, weil__dict__
ist nicht geschaffen für jede Instanz.
Option: Mit einem dataclass (Python 3.7+)
Mit Python 3.7 neue dataclasses, zurückzukehren, eine Klasse, mit automatisch Hinzugefügt speziellen Methoden, schreib-und andere nützliche tools:
@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)
Option: Mit Hilfe einer Liste
Einen anderen Vorschlag, habe ich übersehen, kommt von Bill, die Eidechse:
def h(x):
result = [x + 1]
result.append(x * 3)
result.append(y0 ** y3)
return result
Dies ist mein wenigsten bevorzugten Methode wenn. Ich glaube, ich bin verdorben durch die Einwirkung von Haskell, aber die Idee des mixed-Typ-Listen hat immer das Gefühl unbequem zu mir. In diesem speziellen Beispiel die Liste ist -nicht - gemischter Typ, aber es denkbar sein könnte.
Einer Liste, die in dieser Art und Weise wirklich nicht alles gewinnen mit Bezug auf die Tupel, so weit ich erzählen kann. Der einzige wirkliche Unterschied zwischen Listen und Tupel in Python ist, dass Listen sind veränderlich, in der Erwägung, dass Tupel nicht.
Ich persönlich tendiere zu führen über die Konventionen aus der funktionalen Programmierung: die Verwendung von Listen für beliebige Anzahl von Elementen des gleichen Typs, und Tupeln wird eine Feste Anzahl von Elementen, die bestimmte Arten.
Frage
Nach der langen Präambel, kommt die unvermeidliche Frage. Welche Methode (denken Sie) am besten ist?
Ich habe in der Regel gefunden, werde mich selbst das Wörterbuch route, denn es handelt sich weniger set-up-Arbeit. Von einem Typen Perspektive allerdings könnten Sie besser dran, wenn man die Klasse route, da dies kann Ihnen helfen, zu vermeiden, verwirrend, was ein Wörterbuch repräsentiert.
Auf der anderen Seite gibt es auch einige in der Python-Gemeinschaft, die das Gefühl haben implizite Schnittstellen sollten bevorzugt werden, um explizite Schnittstellen, an welchem Punkt der Typ des Objekts ist nicht wirklich relevant, da Sie im wesentlichen unter Berufung auf die Konvention, dass das gleiche Attribut haben stets die gleiche Bedeutung.
So, wie Sie es tun-Sie - return mehrere Werte in Python?
- In Ihnen hervorragende Beispiele, die Sie verwenden variable
y3
, aber es sei denn, y3 ist global deklariert, dies würde die AusbeuteNameError: global name 'y3' is not defined
vielleicht verwenden Sie einfach3
?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Benannte Tupel wurden in 2.6 für diesen Zweck. Siehe auch os.stat für eine ähnliche builtin Beispiel.
In den letzten Versionen von Python 3 (3.6+, denke ich), die neue
typing
Bibliothek bekam dieNamedTuple
Klasse zu machen, benannten Tupel einfacher und mehr leistungsstarke. Erben vontyping.NamedTuple
ermöglicht Ihnen die Verwendung von docstrings, default-Werte, und geben Sie Anmerkungen ein.Beispiel (Aus der Dokumentation):
namedtuple
ist mit einem kleineren Speicher-footprint für Masse Ergebnisse (lange Listen von Tupeln, wie die Ergebnisse der DB-Abfragen). Für einzelne Elemente (wenn die Funktion in Frage, die nicht oft aufgerufen) - Wörterbücher und-Klassen sind auch gut. Aber namedtuples sind eine schöne/schönere Lösung auch in diesem Fall.namedtuple
Definitionen (jeder Aufruf erzeugt eine neue), die Schaffung dernamedtuple
Klasse ist verhältnismäßig teuer, CPU und Arbeitsspeicher, und alle Klassendefinitionen intrinsisch umfassen zyklische Referenzen (also auf CPython, warten Sie für eine zyklische GC führen für Sie freigelassen). Es macht es auch unmöglichpickle
die Klasse (und daher unmöglich zu nutzen, Instanzen mitmultiprocessing
in den meisten Fällen). Jede Kreation der Klasse, die auf meine 3.6.4 x64 verbraucht ~A. 0,337 ms, und belegt knapp 1 KB Speicher, töten jede Instanz Einsparungen.namedtuple
Klassen; die CPU-Kosten sinken um etwa einen Faktor 4x, aber Sie sind immer noch ungefähr 1000x höher als die Kosten für eine Instanz zu schaffen, und die Speicher-Kosten für jede Klasse bleibt hoch (ich war falsch in meinem letzten Kommentar über "unter 1 KB" für die Klasse_source
selbst ist in der Regel 1,5 KB;_source
entfernt 3.7, so ist es wahrscheinlich näher an der ursprünglichen Forderung von knapp unter 1 KB pro Klasse Kreation).import collections
. Deshalb halte ich die Verwendung von Wörterbüchern.Für kleine Projekte finde ich es am einfachsten, mit zu arbeiten-Tupeln. Wenn das wird zu schwer zu verwalten (und nicht vor) ordne ich die Dinge in logische Strukturen, aber ich denke, dass Ihr vorgeschlagener Gebrauch von Wörterbüchern und ReturnValue Objekte ist falsch (oder zu simpel).
Rückkehr ein dictionary mit den keys y0, y1, y2, etc. bietet keinen Vorteil über Tupel. Rückkehr ein ReturnValue-Instanz mit Eigenschaften .y0, .y1, .y2, etc. bietet keinen Vorteil gegenüber der Tupel entweder. Sie müssen zu Beginn der Benennung der Dinge, wenn Sie wollen, um überall zu bekommen, und Sie können tun, dass mit Tupeln sowieso:
IMHO, die nur gute Technik jenseits von Tupeln ist die Rückkehr von realen Objekten mit geeigneten Methoden und Eigenschaften, wie Sie von
re.match()
oderopen(file)
.size, type, dimensions = getImageData(x)
und(size, type, dimensions) = getImageData(x)
? I. e., nicht einwickeln Links-hand-Seite eine tupled Abtretung das einen Unterschied machen?(1)
ist ein int bei(1,)
oder1,
ist ein Tupel.Viele der Antworten vorschlagen, die Sie brauchen, um wieder eine Sammlung irgendeiner Art, wie ein Wörterbuch oder eine Liste. Sie verlassen konnten aus der zusätzlichen syntax und schreiben Sie einfach die return-Werte werden durch Kommas getrennt. Hinweis: diese technisch gibt ein Tupel.
gibt:
type(f())
zurück<class 'tuple'>
.tuple
Aspekt explizit; es ist nicht wirklich wichtig, dass Sie zurückgeben, einetuple
dies ist das idiom für die Rückgabe mehrere Werte Zeitraum. Gleichen Grund, warum Sie das weglassen der Klammern mit der swap-idiom,x, y = y, x
mehrere Initialisierungx, y = 0, 1
, etc.; sicher, es machttuple
s unter der Haube, aber es gibt keinen Grund für die explizite, da dietuple
s nicht der Punkt, an alle. Das Python-tutorial stellt mehrfache Zuordnung lange, bevor es selbst berührttuple
s.=
ist ein Tupel in Python mit oder ohne Klammer um Sie herum. Also gibt es eigentlich keine expliziten oder impliziten hier. a,b,c ist, wie sehr ein Tupel (a,b,c). Auch gibt es kein making of tuple "unter der Haube", wenn Sie wieder solche Werte, da es nur ein schlichter Tupel. Die OP bereits erwähnt Tupel, so gibt es eigentlich keinen Unterschied zwischen dem, was er erwähnt und was diese Antwort zeigt. KeinerVote ich für das Wörterbuch.
Ich finde, dass wenn ich eine Funktion zurückgibt, die etwas mehr als 2-3 Variablen werde ich Falten Sie Sie in einem Wörterbuch nach. Ansonsten Neige ich dazu, zu vergessen, die Reihenfolge und der Inhalt von dem, was ich fahre.
Auch, die Einführung einer "besonderen" Struktur macht den code schwerer zu Folgen. (Jemand anderes suchen, durch den code, um herauszufinden, was es ist)
Wenn Ihr besorgt über Aussehen, verwenden Sie beschreibende Wörterbuch Schlüssel, Beispiel: 'x-Werte-Liste".
result = g(x); other_function(result)
Eine andere Möglichkeit wäre die Verwendung von Generatoren:
Obwohl IMHO Tupel sind in der Regel am besten, außer in Fällen, in denen die Werte, die zurückgegeben werden, sind Kandidaten für die Verkapselung in eine Klasse.
yield
?def f(x): …; yield b; yield a; yield r
vs.(g for g in [b, a, r])
, und beide leicht konvertieren zu Listen oder Tupeln, und als solche unterstützt tuple unpacking. Die Tupel von generator-form folgt einem funktionalen Ansatz, während die Funktion Formular ist zwingend notwendig und erlauben flow control und variable Zuordnung.Ich lieber mit Tupeln, wenn ein Tupel fühlt sich "natürlicher"; Koordinaten sind ein typisches Beispiel, wo die Objekte trennen zu können, stehen auf Ihren eigenen, zum Beispiel in einem-Achse Skalierung Berechnungen, und die Reihenfolge ist wichtig. Hinweis: wenn kann ich Sortieren oder mischen die Elemente ohne einen nachteiligen Effekt auf die Bedeutung der Gruppe, dann habe ich wohl nicht verwenden ein Tupel.
Benutze ich Wörterbücher als return-Wert nur, wenn die gruppierten Objekte nicht immer das gleiche. Denken Sie optionale E-Mail-Header.
Für den rest der Fälle, in denen die gruppierten Objekte haben inhärente Bedeutung innerhalb der Gruppe oder ein vollwertiges Objekt mit eigenen Methoden, die benötigt wird, verwende ich eine Klasse.
Ich bevorzuge:
Es scheint, alles andere ist nur zusätzlicher code, das gleiche zu tun.
In der Regel, die "spezielle Struktur" IST eigentlich eine sinnvolle aktuelle Zustand eines Objekts, mit seinen eigenen Methoden.
Ich mag, um Namen zu finden, die für den anonymen Strukturen, wo möglich. Sinnvolle Namen machen die Dinge mehr klar.
Python-tuples, dicts und Objekte bieten dem Programmierer einen reibungslosen Kompromiss zwischen Formalität und Komfort für die kleinen Daten-Strukturen ("Dinge"). Für mich, die Entscheidung, wie Sie repräsentieren eine Sache wird bestimmt vor allem, wie werde ich die Struktur. In C++, es ist eine gängige Konvention zu verwenden
struct
für Daten-only-items undclass
für Objekte mit Methoden, auch wenn Sie rechtlich Methoden auf einestruct
; meine Gewohnheit ist ähnlich wie in Python, mitdict
undtuple
im Ortstruct
.Für Koordinaten-sets, verwende ich ein
tuple
eher als einen Punktclass
oder einedict
(und beachten Sie, dass Sie einetuple
als dictionary-Schlüssel, sodict
s machen große sparse multidimensional arrays).Wenn ich werde sein, der Iteration über eine Liste der Dinge, die ich lieber Auspacken
tuple
s auf die iteration:...wie die Objekt-version ist übersichtlicher zu Lesen:
...geschweige denn die
dict
.Wenn die Sache ist weit verbreitet, und Sie finden sich dabei ähnliche nicht-triviale Operationen auf es an mehreren stellen im code, dann ist es meist lohnend zu machen, ein Klasse-Objekt mit den entsprechenden Methoden.
Schließlich, wenn ich bin gehen, um den Austausch von Daten mit nicht-Python-system-Komponenten, ich werde sehr oft halten Sie Sie in einem
dict
weil das ist am besten geeignet, um JSON-Serialisierung.+1 auf S. Lott ' s Vorschlag eines namens container-Klasse.
Für Python 2.6 und höher, eine benannte Tupel bietet eine nützliche Möglichkeit, einfach die Erstellung dieser container-Klassen, und die Ergebnisse sind "leicht und erfordern nicht mehr Speicher als der reguläre Tupel".
In Sprachen wie Python, ich würde in der Regel verwenden Sie ein Wörterbuch, wie es erfordert weniger Aufwand als eine neue Klasse zu erstellen.
Allerdings, wenn ich ständig die Rückgabe der gleichen Menge von Variablen, dann wahrscheinlich beinhaltet eine neue Klasse, die werde ich Faktor aus.
Ich mit einem dict übergeben und zurückgeben von Werten aus einer Funktion:
Verwendung variabler form gemäß der Definition in form.
Dies ist der effizienteste Weg für mich und für die processing unit.
Übergibt man nur einen Zeiger an und kehren nur einem Zeiger aus.
Du nicht ändern-Funktionen " (Tausende davon) Argumente, Wann immer Sie eine änderung in Ihrem code.
test
direkt den Wert verändern. Vergleichen Sie dies mitdict.update
, die nicht einen Wert zurückgeben.form
verletzt Lesbarkeit noch die Leistung. In den Fällen, wo Sie brauchen, um neu zu formatierenform
, die Rückgabe [der form] bedeutet stellen Sie sicher, dass die letztenform
zurückgegeben wird, weil Sie nicht halten Spur von form Veränderungen überall."Beste" ist eine teilweise subjektive Entscheidung. Verwenden Tupel für kleine Rückkehr setzt im Allgemeinen Fall, wo eine unveränderliche ist akzeptabel. Ein Tupel ist immer besser, eine Liste, wenn Veränderlichkeit ist nicht Voraussetzung.
Weitere komplexe, die Werte zurückgeben, oder für den Fall, wo Formalität ist wertvoll (d.h. hoher Wert, code) ein benanntes Tupel ist besser. Für die meisten komplexen Fall ein Objekt in der Regel am besten. Jedoch, es ist wirklich die situation, dass Fragen. Wenn es Sinn macht, ein Objekt zurückgeben, weil das ist, was Sie natürlich haben, am Ende der Funktion (z.B. Factory pattern), dann das Objekt zurückgeben.
Als weiser Mann sagte: