Est-ce que c'est pythonic pour une fonction de retourner plusieurs valeurs?

En python, vous pouvez avoir une fonction de renvoyer plusieurs valeurs. Voici un exemple inventé:

def divide(x, y):
    quotient = x/y
    remainder = x % y
    return quotient, remainder  

(q, r) = divide(22, 7)

cela semble très utile, mais il semble qu'il peut également être abusé ("Eh bien..la fonction X calcule déjà ce dont nous avons besoin comme valeur intermédiaire. Laissons X retourner cette valeur aussi").

Quand devez-vous tracer la ligne et définir une méthode différente?

73
demandé sur ShadowRanger 2008-09-15 00:15:19

9 réponses

Absolument (pour l'exemple que vous avez fournies.

Tuples sont des citoyens à part entière en Python

il y a une fonction intégrée divmod() qui fait exactement cela.

q, r = divmod(x, y) # ((x - x%y)/y, x%y) Invariant: div*y + mod == x

il y a d'autres exemples: zip , enumerate , dict.items .

for i, e in enumerate([1, 3, 3]):
    print "index=%d, element=%s" % (i, e)

# reverse keys and values in a dictionary
d = dict((v, k) for k, v in adict.items()) # or 
d = dict(zip(adict.values(), adict.keys()))

BTW, les parenthèses ne sont pas nécessaires la plupart du temps. Citation de Python Référence De La Bibliothèque :

Tuples peuvent être construites dans un certain nombre de façons:

  • utilisant une paire de parenthèses pour désigner le tuple vide: ()
  • utilisant une virgule pour un tuple singleton: a, ou (a,)
  • séparant les articles avec des virgules: a, b, C ou (a, b, c)
  • utilisant le tuple() intégré: tuple () ou tuple (iterable)

Fonctions doivent servir un but unique

par conséquent, ils doivent retourner un seul objet. Dans votre cas, cet objet est un n-uplet. Considérer le tuple comme une structure de données composée ad hoc. Il y a des langues où presque chaque fonction renvoie plusieurs valeurs (liste dans Lisp).

il suffit parfois de retourner (x, y) au lieu de Point(x, y) .

nommé tuples

avec l'introduction des tuples nommés en Python 2.6, il est préférable dans de nombreux cas de retourner les tuples nommés au lieu des tuples simples.

>>> import collections
>>> Point = collections.namedtuple('Point', 'x y')
>>> x, y = Point(0, 1)
>>> p = Point(x, y)
>>> x, y, p
(0, 1, Point(x=0, y=1))
>>> p.x, p.y, p[0], p[1]
(0, 1, 0, 1)
>>> for i in p:
...   print(i)
...
0
1
97
répondu jfs 2018-08-29 00:28:37

tout D'abord, notez que Python permet ce qui suit (pas besoin de la parenthèse):

q, r = divide(22, 7)

en ce qui concerne votre question, il n'y a pas de règle absolue. Pour des exemples simples (et généralement inventés), il peut sembler qu'il est toujours possible pour une fonction donnée d'avoir un but unique, résultant en une seule valeur. Cependant, lorsque vous utilisez Python pour des applications réelles, vous rencontrez rapidement de nombreux cas où il est nécessaire de retourner plusieurs valeurs., et plus propre code.

donc, je dirais, faites ce qui a du sens, et n'essayez pas de vous conformer à une convention artificielle. Python supporte plusieurs valeurs de retour, donc utilisez-le quand c'est approprié.

26
répondu Jason Etheridge 2008-09-15 03:44:55

l'exemple que vous donnez est en fait une fonction intégrée à python, appelée divmod . Donc quelqu'un, à un moment donné, a pensé que c'était assez pythonique pour inclure dans la fonctionnalité de base.

pour moi, si ça rend le code plus propre, c'est pythonique. Comparez ces deux blocs de code:

seconds = 1234
minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)

seconds = 1234
minutes = seconds / 60
seconds = seconds % 60
hours = minutes / 60
minutes = minutes % 60
12
répondu Nathan Jones 2008-09-14 20:55:28

Oui, retourner plusieurs valeurs (i.e., un tuple) est certainement pythonique. Comme d'autres l'ont fait remarquer, il y a beaucoup d'exemples dans la bibliothèque standard de Python, ainsi que dans les projets Python très respectés. Deux autres commentaires:

  1. Retourner plusieurs valeurs est parfois très, très utile. Prenez, par exemple, une méthode qui gère optionnellement un événement (en retournant une certaine valeur en le faisant) et qui retourne aussi le succès ou l'échec. Cela peut survenir dans un modèle chaîne de responsabilité. Dans d'autres cas, vous voulez retourner plusieurs, étroitement liée morceaux de données---comme dans l'exemple donné. Dans ce paramètre, retourner plusieurs valeurs est similaire à retourner une instance unique d'une classe anonyme avec plusieurs variables membres.
  2. la manipulation par Python des arguments de méthode nécessite la possibilité de retourner directement des valeurs multiples. Dans C++, par exemple, les arguments de méthode peuvent être passés par référence, donc vous pouvez attribuez-leur des valeurs de sortie, en plus de la valeur de retour formelle. En Python, les arguments sont passés "par référence" (mais dans le sens de Java, pas C++). Vous ne pouvez pas attribuer de nouvelles valeurs aux arguments de méthode et les faire refléter en dehors de la portée de la méthode. Par exemple:

    // C++
    void test(int& arg)
    {
        arg = 1;
    }
    
    int foo = 0;
    test(foo); // foo is now 1!
    

    comparer avec:

    # Python
    def test(arg):
        arg = 1
    
    foo = 0
    test(foo) # foo is still 0
    
3
répondu zweiterlinde 2012-10-21 08:27:19

c'est définitivement pythonique. Le fait que vous pouvez retourner plusieurs valeurs à partir d'une fonction le boilerplate que vous auriez dans un langage comme C où vous devez définir une structure pour chaque combinaison de types que vous retournez quelque part.

cependant, si vous atteignez le point où vous retournez quelque chose de fou comme 10 valeurs à partir d'une seule fonction, vous devriez sérieusement envisager de les regrouper dans une classe parce qu'à ce moment-là, il devient lourd.

1
répondu indentation 2008-09-15 14:46:06

retourner un tuple, c'est cool. Notez également la nouvelle gamme qui a été ajouté en python 2.6 ce qui peut rendre ce plus agréable pour vous: http://docs.python.org/dev/library/collections.html#collections.namedtuple

1
répondu pixelbeat 2008-09-15 15:14:04

je suis assez nouveau en Python, mais la technique des tuples me semble très pythonique. Cependant, j'ai eu une autre idée qui pourrait améliorer la lisibilité. L'utilisation d'un dictionnaire permet d'accéder aux différentes valeurs par nom plutôt que par position. Par exemple:

def divide(x, y):
    return {'quotient': x/y, 'remainder':x%y }

answer = divide(22, 7)
print answer['quotient']
print answer['remainder']
1
répondu Fred Larson 2008-09-15 21:05:57

OT: RSRE de Algol68 a la curieuse "/:=" opérateur. par exemple.

INT quotient:=355, remainder;
remainder := (quotient /:= 113);

donnant un quotient de 3, et un reste de 16.

Note: typiquement la valeur de "(x/:=y)" est écartée car le quotient "x" est attribué par référence, mais dans le cas de RSRE la valeur retournée est le reste.

C. F. Arithmétique Entière-Algol68

1
répondu NevilleDNZ 2009-03-12 21:48:42

c'est bien de retourner des valeurs multiples en utilisant un tuple pour des fonctions simples telles que divmod . S'il rend le code lisible, C'est Pythonic.

si la valeur de retour commence à devenir confuse, vérifiez si la fonction en fait trop et divisez-la si elle l'est. Si un grand tuple est utilisé comme un objet, en faire un objet. Aussi, envisagez d'utiliser nommé tuples , qui fera partie de la bibliothèque standard en python 2.6.

0
répondu Will Harris 2008-09-15 14:43:01