Question

En python, vous pouvez faire en sorte qu'une fonction retourne plusieurs valeurs. Voici un exemple artificiel:

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’on puisse aussi en abuser (la fonction "Bien ... X calcule déjà ce dont nous avons besoin en tant que valeur intermédiaire. Laissons X renvoyer cette valeur également").

Quand faut-il tracer la ligne et définir une méthode différente?

Était-ce utile?

La solution

Absolument (pour l'exemple que vous avez fourni).

Les tuples sont des citoyens de première classe en Python

Il existe 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 existe d'autres exemples: zip , énumérer , 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 tirée de Référence de la bibliothèque Python :

  

Les tuples peuvent être construits de différentes manières:

     
      
  • Utilisez une paire de parenthèses pour indiquer le tuple vide: ()
  •   
  • Utilisation d'une virgule de fin pour un tuple de singleton: a, ou (a,)
  •   
  • Séparation des éléments par des virgules: a, b, c ou (a, b, c)
  •   
  • Utilisation du tuple () intégré: tuple () ou tuple (iterable)
  •   

Les fonctions doivent avoir un seul but

Par conséquent, ils doivent renvoyer un seul objet. Dans votre cas, cet objet est un tuple. Considérez tuple comme une structure de données composée ad-hoc. Il existe des langues où presque chaque fonction renvoie plusieurs valeurs (list en Lisp).

Parfois, il suffit de renvoyer (x, y) au lieu de Point (x, y) .

Tuples nommés

Avec l'introduction des tuples nommés dans Python 2.6, il est préférable dans de nombreux cas de renvoyer des tuples nommés au lieu de tuples ordinaires.

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

Autres conseils

Tout d'abord, notez que Python permet les opérations suivantes (inutile de la parenthèse):

q, r = divide(22, 7)

En ce qui concerne votre question, il n’ya pas de règle absolue. Pour des exemples simples (et généralement artificiels), il peut sembler qu'il est toujours possible pour une fonction donnée d'avoir un seul but, aboutissant à une seule valeur. Cependant, lorsque vous utilisez Python pour des applications réelles, vous rencontrez rapidement plusieurs cas dans lesquels il est nécessaire de renvoyer plusieurs valeurs, ce qui entraîne un code plus propre.

Donc, je dirais que faire ce qui a du sens et ne pas essayer de se conformer à une convention artificielle. Python prend en charge plusieurs valeurs de retour, utilisez-le dès que nécessaire.

L'exemple que vous donnez est en fait une fonction intégrée à python, appelée divmod . Donc, à un moment donné, quelqu'un a pensé qu'il était assez pythonique pour être inclus dans les fonctionnalités principales.

Pour moi, si cela rend le code plus propre, il 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

Oui, le renvoi de plusieurs valeurs (c’est-à-dire un tuple) est définitivement pythonique. Comme d'autres l'ont souligné, il existe de nombreux exemples dans la bibliothèque standard Python, ainsi que dans des projets Python bien respectés. Deux commentaires supplémentaires:

  1. Il est parfois très utile de renvoyer plusieurs valeurs. Prenons, par exemple, une méthode qui gère éventuellement un événement (en renvoyant une valeur) et renvoie également succès ou échec. Cela pourrait se produire dans un modèle de chaîne de responsabilité. Dans d'autres cas, vous souhaitez renvoyer plusieurs données étroitement liées, comme dans l'exemple donné. Dans ce paramètre, renvoyer plusieurs valeurs revient à renvoyer une seule instance d'une classe anonyme avec plusieurs variables membres.
  2. La gestion par Python des arguments de méthode nécessite la possibilité de renvoyer directement plusieurs valeurs. En C ++, par exemple, les arguments de méthode peuvent être passés par référence afin que vous puissiez leur affecter 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 de C ++). Vous ne pouvez pas affecter 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!
    

    Comparez avec:

    # Python
    def test(arg):
        arg = 1
    
    foo = 0
    test(foo) # foo is still 0
    

C'est définitivement pythonique. Le fait que vous puissiez renvoyer plusieurs valeurs d’une fonction que vous auriez dans un langage comme C où vous devez définir une structure pour chaque combinaison de types que vous renvoyez quelque part.

Toutefois, si vous renvoyez quelque chose de fou comme 10 valeurs d'une seule fonction, vous devez sérieusement envisager de les regrouper dans une classe car à ce stade, cela devient difficile à manier.

Retourner un tuple, c'est cool. Notez également le nouveau nom nommé qui a été ajouté dans python 2.6, ce qui peut rendre cela plus agréable pour vous: http://docs.python.org/dev/library/collections. html # collections.namedtuple

OT: Algol68 de RSRE a le curieux "/: =" opérateur. par exemple.

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

Donner un quotient de 3 et un reste de 16.

Remarque: typiquement la valeur de "(x /: = y)" est rejeté en tant que quotient " x " est attribué par référence, mais dans le cas de RSRE, la valeur renvoyée est le reste.

c.f. Arithmétique de nombre entier - Algol68

C'est bien de renvoyer plusieurs valeurs en utilisant un tuple pour des fonctions simples telles que divmod . Si cela 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 c'est le cas. Si un grand tuple est utilisé comme un objet, faites-en un objet. Pensez également à utiliser les tuples nommés , qui feront partie de la bibliothèque standard de Python 2.6.

Je connais assez bien Python, mais la technique du tuple 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']
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top