retour, retour Aucun, et pas de retour du tout?

386

Considérez trois fonctions:

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

def my_func2():
  print "Hello World"
  return

def my_func3():
  print "Hello World"

Ils semblent tous retourner Aucun. Existe-t-il des différences entre le comportement de la valeur renvoyée de ces fonctions? Y a-t-il des raisons de préférer l'un à l'autre?

Clay Wardell
la source
40
Notez qu'il y a une différence stylistique. return Noneimplique pour moi que la fonction a parfois une Nonevaleur de non- retour, mais à l'emplacement de return None, il n'y a pas une telle valeur de retour. Ecrire non returnimplique pour moi qu'il n'y a jamais de valeur de retour intéressante, un peu comme une "procédure" par opposition à une "fonction". returnimplique d'exister très tôt à partir d'une "procédure" selon le point précédent.

Réponses:

500

Sur le comportement réel, il n'y a pas de différence. Ils reviennent tous Noneet c'est tout. Cependant, il y a un temps et un lieu pour tout cela. Les instructions suivantes indiquent essentiellement comment les différentes méthodes doivent être utilisées (ou du moins comment on m'a appris qu'elles devraient être utilisées), mais ce ne sont pas des règles absolues, vous pouvez donc les mélanger si vous le jugez nécessaire.

En utilisant return None

Cela indique que la fonction est en effet destinée à renvoyer une valeur pour une utilisation ultérieure, et dans ce cas, elle retourne None. Cette valeur Nonepeut ensuite être utilisée ailleurs. return Nonen'est jamais utilisé s'il n'y a pas d'autres valeurs de retour possibles de la fonction.

Dans l'exemple suivant, nous retournons personle s mothersi le persondonné est un humain. Si ce n'est pas un humain, nous revenons Nonecar le personn'a pas mother(supposons que ce n'est pas un animal ou quelque chose).

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

En utilisant return

Ceci est utilisé pour la même raison que breakdans les boucles. La valeur de retour n'a pas d'importance et vous souhaitez uniquement quitter la fonction entière. Il est extrêmement utile à certains endroits, même si vous n'en avez pas besoin souvent.

Nous en avons 15 prisonerset nous savons que l'un d'eux a un couteau. Nous passons en revue chacun prisonerd'eux pour vérifier s'ils ont un couteau. Si nous frappons la personne avec un couteau, nous pouvons simplement quitter la fonction parce que nous savons qu'il n'y a qu'un seul couteau et aucune raison de vérifier le reste du prisoners. Si nous ne trouvons pas le prisonercouteau, nous lançons une alerte. Cela peut être fait de différentes manières et l'utilisation returnn'est probablement même pas la meilleure façon, mais ce n'est qu'un exemple pour montrer comment l'utiliser returnpour quitter une fonction.

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()

Remarque: vous ne devriez jamais le faire var = find_prisoner_with_knife(), car la valeur de retour n'est pas destinée à être interceptée.

Ne pas utiliser returndu tout

Cela reviendra également None, mais cette valeur n'est pas destinée à être utilisée ou capturée. Cela signifie simplement que la fonction s'est terminée avec succès. C'est fondamentalement la même chose que returndans les voidfonctions de langages tels que C ++ ou Java.

Dans l'exemple suivant, nous définissons le nom de la mère de la personne, puis la fonction se termine après avoir terminé avec succès.

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

Remarque: vous ne devriez jamais le faire var = set_mother(my_person, my_mother), car la valeur de retour n'est pas destinée à être interceptée.

Josh Downes
la source
5
var = get_mother()est OK si vous passez varà d'autres fonctions qui acceptent None- "jamais" est un peu extrême
joelb
Comment accepter la valeur de retour si elle var = get_mother()ne peut pas être utilisée? À mon humble avis, c'est très bien de le faire. Vous devez juste vous assurer de vérifier une non Nonevaleur avec if varavant de l'utiliser.
winklerrr
J'ai pensé qu'il était important de mentionner que ce n'est pas seulement une bonne convention, elle est exigée par PEP8. Je poste une réponse pour complimenter la vôtre et cite PEP8 à ce sujet.
Fabiano
29

Oui, ils sont tous pareils.

Nous pouvons examiner le code machine interprété pour confirmer qu'ils font tous exactement la même chose.

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
la source
4
attention ici ... dis.disretourne None: -X
mgilson
La seule façon d'obtenir la sortie de dis sous forme de chaîne est de rediriger stdout vers une sorte de tampon d'E / S (par exemple StringIO). Même dans ce cas, la comparaison directe pourrait ne pas fonctionner, car je pense dis.diségalement que certains numéros de ligne sont
rapportés
Peu importe qu'ils utilisent ou non les mêmes registres de toute façon, j'ai donc changé mon code afin que nous regardions maintenant le code de la machine au lieu de faire une sorte de comparaison de chaînes maladroite. Merci pour le très gros heads up.
David Marx
19

Ils retournent chacun le même singleton None- Il n'y a pas de différence fonctionnelle.

Je pense qu'il est raisonnablement idiomatique de laisser la returndéclaration à moins que vous n'en ayez besoin pour sortir de la fonction plus tôt (auquel cas une mise à nu returnest plus courante), ou renvoyer autre chose que None. Il est également logique et semble idiomatique d'écrire return Nonequand il se trouve dans une fonction qui a un autre chemin qui renvoie autre chose queNone . L'écriture return Noneexplicite est une indication visuelle pour le lecteur qu'il existe une autre branche qui renvoie quelque chose de plus intéressant (et que le code appelant devra probablement gérer les deux types de valeurs de retour).

Souvent en Python, les fonctions qui retournent Nonesont utilisées comme des voidfonctions en C - Leur but est généralement d'opérer sur les arguments d'entrée en place (sauf si vous utilisez des données globales ( frissons )). Le retour Nonerend généralement plus explicite la mutation des arguments. Cela rend un peu plus clair pourquoi il est logique de laisser l' returnénoncé du point de vue des «conventions linguistiques».

Cela dit, si vous travaillez dans une base de code qui a déjà des conventions prédéfinies autour de ces choses, je ferais certainement de même pour aider la base de code à rester uniforme ...

mgilson
la source
1
Ou plus généralement, chaque fois qu'une fonction se termine sans toucher une returninstruction, elle revient None.
Il n'est pas idiomatique d'omettre l'instruction return si la fonction doit renvoyer une valeur. Seules les fonctions qui fonctionnent uniquement via des effets secondaires doivent omettre l'instruction return.
molécule
@molecule - Ouais, je pense que je n'ai peut-être pas fait le meilleur travail dans mon explication ici. J'ai essayé de le mettre à jour sans en faire un clone complet de la (bien meilleure) réponse ci-dessus. Je ne suis toujours pas super content de ce post ... Une partie de moi veut le supprimer (car la réponse ci-dessus est tellement meilleure) et une partie de moi veut le garder - jusqu'à présent, 9 personnes ont pensé que c'était bon pour une raison ou une autre. Peut-être que j'ai frappé quelque chose que quelqu'un d'autre a manqué?
mgilson
1
@ZAB - Je ne pense pas que ce soit possible. En python, je peux patcher n'importe quoi avec n'importe quoi (c'est par conception et c'est une excellente fonctionnalité si elle est utilisée avec parcimonie). Plus précisément, je peux patcher une fonction qui a un retour avec une qui n'en a pas. Toutes les vérifications «pas une expression» se produisent au moment de l'analyse, mais en raison de l'application de correctifs de singe, cela ne peut pas se produire avant l'exécution. IOW, c'est complètement différent. Personnellement, je pense que le comportement actuel est tout à fait raisonnable (et cohérent avec les autres langages de haut niveau), mais je ne suis pas celui que vous devez convaincre :-).
mgilson
1
@ZAB - les deux Rubyet Javascriptadoptent la même approche que python. Je suis sûr qu'il ya des exemples de langages de haut niveau qui disent « vide » dans l'expression , mais je ne peux pas penser à tout au moment (peut - être si l' on considère Javaun langage de haut niveau) ... Comment serait singe-patcher une fonction (qui est utile pour se moquer entre autres des tests) fonctionne-t-elle dans un monde hypothétique où python a un voidmot-clé qui fait en sorte que la fonction ne retourne rien? (De plus, comme je l'ai déjà dit, vous n'avez pas besoin de me convaincre qu'il s'agit d'un mauvais design. Je ne peux rien faire même si vous avez raison)
mgilson
4

Comme d'autres l'ont répondu, le résultat est exactement le même, Noneest retourné dans tous les cas.

La différence est stylistique, mais veuillez noter que PEP8 nécessite une utilisation cohérente:

Soyez cohérent dans les déclarations de retour. Soit toutes les instructions de retour d'une fonction doivent renvoyer une expression, soit aucune ne doit le faire. Si une instruction return renvoie une expression, toute instruction return où aucune valeur n'est renvoyée doit l'indiquer explicitement comme return None, et une instruction return explicite doit être présente à la fin de la fonction (si elle est accessible).

Oui:

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)

Non:

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


Fondamentalement, si vous retournez une Nonevaleur nulle dans une fonction, cela signifie que la valeur de retour a un sens et est destinée à être interceptée par les appelants. Donc, quand vous revenez None, il doit aussi être explicite, pour transmettreNone dans ce cas a du sens, c'est une des valeurs de retour possibles.

Si vous n'avez pas besoin du tout de retour, votre fonction fonctionne essentiellement comme une procédure au lieu d'une fonction, donc n'incluez pas l' returninstruction.

Si vous écrivez une fonction de type procédure et qu'il est possible de revenir plus tôt (c'est-à-dire que vous avez déjà terminé à ce stade et n'avez pas besoin d'exécuter le reste de la fonction), vous pouvez utiliser un returns vide pour signaler au lecteur c'est juste une fin précoce de l'exécution et la Nonevaleur retournée implicitement n'a aucune signification et n'est pas destinée à être interceptée (la fonction de type procédure retourne toujours de Nonetoute façon).

Fabiano
la source
3

En termes de fonctionnalité, ce sont tous les mêmes, la différence entre eux réside dans la lisibilité et le style du code (ce qui est important à considérer)

Yehuda Schwartz
la source