1148 votes

Comment renvoyer plusieurs valeurs en Python ?

La façon canonique de retourner des valeurs multiples dans les langues qui le supportent est souvent Tupling .

Considérons cet exemple trivial :

def f(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return (y0,y1,y2)

Toutefois, cela devient rapidement problématique lorsque le nombre de valeurs renvoyées augmente. Que se passe-t-il si vous voulez renvoyer quatre ou cinq valeurs ? Bien sûr, vous pouvez continuer à les regrouper, mais il est facile d'oublier quelle valeur se trouve à quel endroit. Il est également assez laid de les déballer là où vous voulez les recevoir.

La prochaine étape logique semble être d'introduire une sorte de "notation d'enregistrement". En python, le moyen le plus évident de le faire est d'utiliser un fichier de type dict .

Considérez ce qui suit :

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0':y0, 'y1':y1 ,'y2':y2 }

(edit- Pour être clair, y0, y1 et y2 sont juste des identifiants abstraits. Comme indiqué, en pratique, vous utiliserez des identifiants significatifs)

Maintenant, nous avons un mécanisme par lequel nous pouvons projeter un membre particulier de l'objet retourné. Par exemple,

result['y0']

Cependant, il existe une autre option. Nous pourrions plutôt retourner une structure spécialisée. J'ai présenté cela dans le contexte de Python, mais je suis sûr que cela s'applique également à d'autres langages. En effet, si vous travaillez en C, cela pourrait très bien être votre seule option. C'est parti :

class ReturnValue(object):
  def __init__(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)

En python, les deux précédentes sont peut-être très similaires en termes de plomberie - Après tout { y0, y1, y2 } finissent par être des entrées dans le système interne __dict__ de la valeur de résultat.

Il existe cependant une fonctionnalité supplémentaire fournie par Python pour les objets minuscules, la fonction __slots__ attribut. La classe pourrait être exprimée comme suit :

class ReturnValue(object):
  __slots__ = ["y0", "y1", "y2"]
  def __init__(y0, y1, y2):
     self.y0 = y0
     self.y1 = y1
     self.y2 = y2

De la Manuel de référence Python :

Le site __slots__ prend une séquence de variables d'instance et réserve juste assez d'espace dans chaque instance pour contenir une valeur pour chaque variable. L'espace est économisé car __dict__ n'est pas créé pour chaque instance.

Une autre suggestion que j'avais négligée vient de Bill le Lézard :

def h(x):
  result = [x + 1]
  result.append(x * 3)
  result.append(y0 ** y3)
  return result

C'est cependant la méthode que je préfère le moins. Je suppose que mon exposition à Haskell m'a influencé, mais l'idée de listes de types mixtes m'a toujours mis mal à l'aise. Dans cet exemple particulier, la liste n'est -pas- de type mixte, mais il est concevable qu'elle puisse l'être. Une liste utilisée de cette manière ne gagne rien par rapport au tuple, pour autant que je sache. La seule véritable différence entre les listes et les tuples en Python est que les listes sont mutable alors que les tuples ne le sont pas. Personnellement, j'ai tendance à reprendre les conventions de la programmation fonctionnelle : utiliser des listes pour un nombre quelconque d'éléments du même type, et des tuples pour un nombre fixe d'éléments de types prédéterminés.

Après le long préambule, vient l'inévitable question. Quelle est (selon vous) la meilleure méthode ?

J'ai généralement choisi la voie du dictionnaire parce qu'elle implique moins de travail de mise en place. Cependant, du point de vue des types, il est préférable d'opter pour les classes, car cela peut vous éviter de confondre ce que représente un dictionnaire. D'un autre côté, certains membres de la communauté Python pensent que les interfaces implicites doivent être préférées aux interfaces explicites Dans ce cas, le type de l'objet n'est pas vraiment pertinent, puisque l'on se base sur la convention selon laquelle le même attribut aura toujours la même signification.

Alors, comment renvoyer plusieurs valeurs en Python ?

661voto

Coady Points 11374

Tuples nommés ont été ajoutés dans la version 2.6 à cette fin. Voir aussi os.stat pour un exemple similaire.

>>> import collections
>>> point = collections.namedtuple('Point', ['x', 'y'])
>>> p = point(1, y=2)
>>> p.x, p.y
1 2
>>> p[0], p[1]
1 2

250voto

too much php Points 27983

Pour les petits projets, je trouve qu'il est plus facile de travailler avec des tuples. Lorsque cela devient trop difficile à gérer (et pas avant), je commence à regrouper les choses dans des structures logiques, mais je pense que l'utilisation des dictionnaires et des objets ReturnValue que vous suggérez est erronée (ou trop simpliste).

Renvoyer un dictionnaire avec des clés y0, y1, y2 etc. n'offre aucun avantage par rapport aux tuples. Renvoyer une instance ReturnValue avec les propriétés .y0 .y1 .y2 etc. n'offre pas non plus d'avantage par rapport aux tuples. Vous devez commencer à nommer les choses si vous voulez arriver à quelque chose, et vous pouvez le faire en utilisant des tuples de toute façon :

def getImageData(filename):
  [snip]
  return size, (format, version, compression), (width,height)
size, type, dimensions = getImageData(x)

À mon avis, la seule bonne technique au-delà des tuples consiste à renvoyer de vrais objets avec des méthodes et des propriétés appropriées, comme c'est le cas avec les éléments suivants re.match() ou open(file) .

79voto

monkut Points 14549

Je vote pour le dictionnaire.

Je trouve que si je fais une fonction qui renvoie quelque chose de plus de 2-3 variables, je les plie dans un dictionnaire. Sinon, j'ai tendance à oublier l'ordre et le contenu de ce que je renvoie.

De plus, l'introduction d'une structure "spéciale" rend votre code plus difficile à suivre. (Quelqu'un d'autre devra chercher dans le code pour savoir de quoi il s'agit).

Si vous êtes préoccupé par la recherche par type, utilisez des clés de dictionnaire descriptives, par exemple, 'x-values list'.

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0':y0, 'y1':y1 ,'y2':y2 }

31voto

tzot Points 32224

Je préfère utiliser des tuples lorsqu'un tuple me semble "naturel" ; les coordonnées sont un exemple typique, où les objets séparés peuvent se suffire à eux-mêmes, par exemple dans les calculs d'échelle à un seul axe.

J'utilise les dictionnaires comme valeur de retour uniquement lorsque les objets groupés ne sont pas toujours les mêmes. Pensez aux en-têtes de courriel optionnels.

Pour le reste des cas, lorsque les objets regroupés ont une signification inhérente à l'intérieur du groupe ou qu'un objet à part entière avec ses propres méthodes est nécessaire, j'utilise une classe.

20voto

John Fouhy Points 14700

+1 sur la suggestion de S.Lott d'une classe de conteneur nommée.

Pour python 2.6 et supérieur, un fichier tuple nommé fournit un moyen utile de créer facilement ces classes de conteneurs, et les résultats sont "légers et ne nécessitent pas plus de mémoire que les tuples ordinaires".

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X