Rückkehr, Rückkehr Keine und überhaupt keine Rückkehr?

386

Betrachten Sie drei Funktionen:

def my_func1():
  print "Hello World"
  return None

def my_func2():
  print "Hello World"
  return

def my_func3():
  print "Hello World"

Sie scheinen alle None zurückzugeben. Gibt es Unterschiede zwischen dem Verhalten des zurückgegebenen Werts dieser Funktionen? Gibt es Gründe, das eine dem anderen vorzuziehen?

Clay Wardell
quelle
40
Beachten Sie, dass es einen stilistischen Unterschied gibt. return Noneimpliziert für mich, dass die Funktion manchmal einen NoneRückgabewert hat, aber an der Stelle von return Nonegibt es keinen solchen Rückgabewert. Nein returnzu schreiben bedeutet für mich, dass es nie einen interessanten Rückgabewert gibt, ein bisschen wie eine "Prozedur" im Gegensatz zu einer "Funktion". returnimpliziert, früh von einem "Verfahren" gemäß dem vorherigen Punkt zu existieren.

Antworten:

500

Beim tatsächlichen Verhalten gibt es keinen Unterschied. Sie alle kehren zurück Noneund das war's. Für all dies gibt es jedoch eine Zeit und einen Ort. Die folgenden Anweisungen beschreiben im Wesentlichen, wie die verschiedenen Methoden verwendet werden sollten (oder zumindest, wie mir beigebracht wurde, dass sie verwendet werden sollten), aber sie sind keine absoluten Regeln, sodass Sie sie verwechseln können, wenn Sie dies für erforderlich halten.

Verwenden von return None

Dies zeigt an, dass die Funktion tatsächlich einen Wert für die spätere Verwendung zurückgeben soll, und in diesem Fall gibt sie zurück None. Dieser Wert Nonekann dann an anderer Stelle verwendet werden. return Nonewird niemals verwendet, wenn keine anderen möglichen Rückgabewerte von der Funktion vorhanden sind.

Im folgenden Beispiel geben wir persons zurück, motherwenn das personGegebene ein Mensch ist. Wenn es kein Mensch ist, kehren wir zurück, Noneda der personkein hat mother(nehmen wir an, es ist kein Tier oder so).

def get_mother(person):
    if is_human(person):
        return person.mother
    else:
        return None

Verwenden von return

Dies wird aus dem gleichen Grund wie breakin Schleifen verwendet. Der Rückgabewert spielt keine Rolle und Sie möchten nur die gesamte Funktion beenden. Es ist an einigen Stellen äußerst nützlich, obwohl Sie es nicht so oft benötigen.

Wir haben 15 prisonersund wir wissen, dass einer von ihnen ein Messer hat. Wir durchlaufen jeden prisonernach dem anderen, um zu überprüfen, ob sie ein Messer haben. Wenn wir die Person mit einem Messer schlagen, können wir die Funktion einfach beenden, weil wir wissen, dass es nur ein Messer gibt und keinen Grund, den Rest zu überprüfen prisoners. Wenn wir das prisonermit einem Messer nicht finden, lösen wir einen Alarm aus. Dies kann auf viele verschiedene Arten geschehen, und die Verwendung returnist wahrscheinlich nicht einmal die beste, aber es ist nur ein Beispiel, um zu zeigen, wie returneine Funktion beendet wird.

def find_prisoner_with_knife(prisoners):
    for prisoner in prisoners:
        if "knife" in prisoner.items:
            prisoner.move_to_inquisition()
            return # no need to check rest of the prisoners nor raise an alert
    raise_alert()

Hinweis: Dies sollten Sie niemals tun var = find_prisoner_with_knife(), da der Rückgabewert nicht zum Abfangen gedacht ist.

Mit nicht returnüberhaupt

Dies wird ebenfalls zurückgegeben None, aber dieser Wert soll nicht verwendet oder abgefangen werden. Es bedeutet einfach, dass die Funktion erfolgreich beendet wurde. Es ist im Grunde das gleiche wie returnbei voidFunktionen in Sprachen wie C ++ oder Java.

Im folgenden Beispiel legen wir den Namen der Mutter der Person fest und die Funktion wird nach erfolgreichem Abschluss beendet.

def set_mother(person, mother):
    if is_human(person):
        person.mother = mother

Hinweis: Dies sollten Sie niemals tun var = set_mother(my_person, my_mother), da der Rückgabewert nicht zum Abfangen gedacht ist.

Josh Downes
quelle
5
var = get_mother()ist in Ordnung, wenn Sie varzu anderen Funktionen None
wechseln
Wie soll man den Rückgabewert akzeptieren, wenn er var = get_mother()nicht verwendet werden kann? IMHO ist es völlig in Ordnung, das zu tun. Sie müssen nur sicherstellen, dass Sie nach einem Nicht- NoneWert suchen, if varbevor Sie ihn verwenden.
Winklerrr
Ich fand es wichtig zu erwähnen, dass dies nicht nur eine gute Konvention ist, sondern von PEP8 gefordert wird. Ich poste eine Antwort, um Ihre zu beglückwünschen, und zitiere PEP8 dazu.
Fabiano
29

Ja, sie sind alle gleich.

Wir können den interpretierten Maschinencode überprüfen, um zu bestätigen, dass alle genau dasselbe tun.

import dis

def f1():
  print "Hello World"
  return None

def f2():
  print "Hello World"
  return

def f3():
  print "Hello World"

dis.dis(f1)
    4   0 LOAD_CONST    1 ('Hello World')
        3 PRINT_ITEM
        4 PRINT_NEWLINE

    5   5 LOAD_CONST    0 (None)
        8 RETURN_VALUE

dis.dis(f2)
    9   0 LOAD_CONST    1 ('Hello World')
        3 PRINT_ITEM
        4 PRINT_NEWLINE

    10  5 LOAD_CONST    0 (None)
        8 RETURN_VALUE

dis.dis(f3)
    14  0 LOAD_CONST    1 ('Hello World')
        3 PRINT_ITEM
        4 PRINT_NEWLINE            
        5 LOAD_CONST    0 (None)
        8 RETURN_VALUE      
David Marx
quelle
4
Vorsicht hier ... dis.diskehrt zurück None: -X
mgilson
Die einzige Möglichkeit, die Ausgabe von dis als Zeichenfolge abzurufen, besteht darin, stdout in eine Art E / A-Puffer (z. B. StringIO) umzuleiten. Selbst dann funktioniert ein direkter Vergleich möglicherweise nicht, da ich glaube, dass dis.disauch einige Zeilennummern
gemeldet
Es ist irrelevant, ob sie sowieso genau die gleichen Register verwenden oder nicht, also habe ich meinen Code geändert, damit wir jetzt nur noch den Maschinencode betrachten, anstatt einen albernen String-Vergleich durchzuführen. Vielen Dank für die sehr großen Köpfe hoch.
David Marx
19

Sie geben jeweils den gleichen Singleton zurück None- Es gibt keinen funktionalen Unterschied.

Ich denke, dass es ziemlich idiomatisch ist, die returnAussage wegzulassen, es sei denn, Sie brauchen sie, um frühzeitig aus der Funktion auszubrechen (in diesem Fall ist ein nackter Fall returnhäufiger) oder etwas anderes als zurückzugeben None. Es macht auch Sinn und scheint idiomatisch zu schreiben, return Nonewenn es sich in einer Funktion befindet, die einen anderen Pfad hat, der etwas anderes zurückgibt als None. Das return Noneexplizite Ausschreiben ist ein visueller Hinweis für den Leser, dass es einen anderen Zweig gibt, der etwas Interessanteres zurückgibt (und dass der aufrufende Code wahrscheinlich beide Arten von Rückgabewerten verarbeiten muss).

Oft in Python, Funktionen , die zurückkehren Nonewerden wie verwendet voidFunktionen in C - Ihr Zweck im Allgemeinen auf die Eingabeargumente für den Betrieb ist in Ort (es sei denn , Sie globale Daten verwenden ( Schauder )). Bei der Rückgabe wird Nonenormalerweise deutlicher, dass die Argumente mutiert wurden. Dies macht etwas klarer, warum es sinnvoll ist, die returnAussage unter dem Gesichtspunkt der "Sprachkonventionen" wegzulassen.

Das heißt, wenn Sie in einer Codebasis arbeiten, die bereits voreingestellte Konventionen für diese Dinge hat, würde ich definitiv nachziehen, um der Codebasis zu helfen, einheitlich zu bleiben ...

mgilson
quelle
1
Oder allgemeiner: Jedes Mal, wenn eine Funktion endet, ohne eine returnAnweisung zu treffen , wird sie zurückgegeben None.
Es ist nicht idiomatisch, die return-Anweisung wegzulassen, wenn von der Funktion erwartet wird, dass sie einen Wert zurückgibt. Nur Funktionen, die ausschließlich durch Nebenwirkungen funktionieren, sollten die return-Anweisung weglassen.
Molekül
@molecule - Ja, ich denke, dass ich in meiner Erklärung hier vielleicht nicht den besten Job gemacht habe. Ich habe versucht, es zu aktualisieren, ohne es zu einem vollständigen Klon der (viel besseren) Antwort oben zu machen. Ich bin immer noch nicht sehr zufrieden mit diesem Beitrag ... Ein Teil von mir möchte ihn löschen (da die Antwort oben so viel besser ist) und ein Teil von mir möchte ihn behalten - 9 Leute haben bisher gedacht, dass er gut für ihn ist der eine oder andere Grund. Vielleicht habe ich etwas getroffen, das jemand anderes verpasst hat?
mgilson
1
@ ZAB - Ich denke nicht, dass das möglich ist. In Python kann ich alles mit irgendetwas patchen (dies ist beabsichtigt und eine großartige Funktion, wenn es sparsam verwendet wird). Insbesondere kann ich eine Funktion, die eine Rückgabe hat, mit einer Funktion versehen, die dies nicht tut. Alle Überprüfungen "kein Ausdruck" werden zur Analysezeit durchgeführt, aber aufgrund von Affen-Patches konnte dies erst zur Laufzeit geschehen. IOW, es ist ganz anders. Persönlich denke ich, dass das aktuelle Verhalten ziemlich vernünftig ist (und mit anderen Hochsprachen übereinstimmt), aber ich bin nicht derjenige, den Sie überzeugen müssen :-).
mgilson
1
@ZAB - beides Rubyund Javascriptder gleiche Ansatz wie bei Python. Ich bin sicher , dass es sind Beispiele für Hochsprachen , die „Leere“ im Ausdruck sagen , aber ich kann von jedem im Moment nicht denken (vielleicht , wenn man bedenkt , Javaeine höhere Programmiersprache) ... Wie würde Affe-Patchen Eine Funktion (die unter anderem zum Verspotten in Tests nützlich ist) funktioniert in einer hypothetischen Welt, in der Python ein voidSchlüsselwort hat, das es so gemacht hat, dass die Funktion nichts zurückgibt? (Wie ich bereits sagte, müssen Sie mich nicht davon überzeugen, dass dies ein schlechtes Design ist. Ich kann nichts tun, auch wenn Sie Recht haben)
mgilson
4

Wie andere geantwortet haben, ist das Ergebnis genau das gleiche, Nonewird in allen Fällen zurückgegeben.

Der Unterschied ist stilistisch, aber bitte beachten Sie, dass PEP8 eine konsistente Verwendung erfordert:

Seien Sie in Rückgabeanweisungen konsistent. Entweder sollten alle return-Anweisungen in einer Funktion einen Ausdruck zurückgeben, oder keine von ihnen sollte. Wenn eine return-Anweisung einen Ausdruck zurückgibt, sollten alle return-Anweisungen, bei denen kein Wert zurückgegeben wird, dies explizit als return None angeben, und am Ende der Funktion sollte eine explizite return-Anweisung vorhanden sein (falls erreichbar).

Ja:

def foo(x):
    if x >= 0:
        return math.sqrt(x)
    else:
        return None

def bar(x):
    if x < 0:
        return None
    return math.sqrt(x)

Nein:

def foo(x):
    if x >= 0:
        return math.sqrt(x)

def bar(x):
    if x < 0:
        return
    return math.sqrt(x)

https://www.python.org/dev/peps/pep-0008/#programming-recommendations


Wenn Sie Nonein einer Funktion jemals einen Nichtwert zurückgeben, bedeutet dies im Grunde, dass der Rückgabewert eine Bedeutung hat und von Anrufern abgefangen werden soll. Wenn Sie also zurückkehren None, muss es auch explizit sein, um Nonein diesem Fall zu vermitteln , dass es einer der möglichen Rückgabewerte ist.

Wenn Sie überhaupt keine Rückgabe benötigen, funktioniert Ihre Funktion im Grunde genommen als Prozedur anstelle einer Funktion. Fügen Sie also die returnAnweisung einfach nicht hinzu .

Wenn Sie eine prozedurähnliche Funktion schreiben und die Möglichkeit besteht, früher zurückzukehren (dh Sie sind zu diesem Zeitpunkt bereits fertig und müssen den Rest der Funktion nicht ausführen), können Sie leere und returns verwenden, um dem Leser ein Signal zu geben Es ist nur ein frühes Ende der Ausführung und der Noneimplizit zurückgegebene Wert hat keine Bedeutung und soll nicht abgefangen werden (die prozedurähnliche Funktion gibt Nonesowieso immer zurück ).

Fabiano
quelle
3

In Bezug auf die Funktionalität sind diese alle gleich, der Unterschied zwischen ihnen liegt in der Lesbarkeit und dem Stil des Codes (was wichtig ist)

Yehuda Schwartz
quelle