Comment renvoyer plusieurs valeurs en Python? [fermé]

la façon canonique de retourner plusieurs valeurs dans les langues qui le supportent est souvent tupling .

Option: utilisation D'un tuple

considérez cet exemple insignifiant:

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

cependant, cela devient rapidement problématique car le nombre de valeurs retournées augmente. Que faire si vous voulez retourner quatre ou cinq valeurs? Bien sûr, vous pouvez continuer à les tupler, mais il est facile d'oublier quelle valeur est où. Il est également assez moche pour décompresser où vous souhaitez les recevoir.

Option: utilisation d'un dictionnaire

La prochaine étape logique semble être d'introduire une sorte de record de notation". En python, la manière évidente de le faire est au moyen d'un dict .

considérer ce qui suit:

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

(modifier - Juste pour être clair, y0, y1 et y2 sont juste comme des identifiants abstraits. Comme fait remarquer, dans la pratique, vous utiliseriez des identificateurs significatifs)

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

result['y0']

Option: Utiliser une classe

cependant, il y a une autre option. Nous pourrions plutôt retourner une structure spécialisée. J'ai encadré dans le contexte de Python, mais je suis sûr que ça s'applique à d'autres langues. En effet, si vous travaillez en C, cela pourrait très bien être votre seule option.

class ReturnValue(object):
  def __init__(self, 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édents sont peut - être très similaires en termes de plomberie-après tout { y0, y1, y2 } juste finir par être des entrées dans le __dict__ interne du ReturnValue .

il y a une caractéristique supplémentaire fournie par Python mais pour les objets minuscules, l'attribut __slots__ . La classe pourrait être exprimée comme suit:

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

de la Python Manuel De Référence :

la déclaration __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 sauvegardé car __dict__ n'est pas créé pour chaque instance.

Option: Utiliser une liste

une autre suggestion que J'avais oublié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 ma méthode préférée. Je suppose que je suis vexé par l'exposition à Haskell, mais l'idée de listes mixtes m'a toujours mis mal à l'aise. Dans cet exemple particulier, la liste n'est pas mixte, mais il pourrait, théoriquement, être. Une liste utilisée de cette façon ne gagne vraiment rien en ce qui concerne le tuple autant que je peux dire. La seule vraie différence entre les listes et les tuples en Python est que les listes sont mutable , wheras les tuples ne le sont pas. Personnellement, j'ai tendance à porter sur les conventions de programmation fonctionnelle: utiliser des listes pour n'importe quel nombre d'éléments du même type, et les tuples pour un nombre fixe d'éléments prédéterminés types.

Question

après le long préambule, vient la question inévitable. Quelle méthode (pensez-vous) est la meilleure?

j'ai l'habitude de me retrouver à suivre le chemin du dictionnaire parce qu'il implique moins de travail de mise en place. Du point de vue des types, cependant, vous pourriez être mieux d'aller dans la voie de la classe, car cela peut vous aider à éviter de confondre ce qu'un dictionnaire représente. D'un autre côté , il y a certains membres de la communauté Python qui pensent que les interfaces implicites devraient être préférées aux interfaces explicites , à ce moment-là le type de l'objet n'est vraiment pas pertinent, puisque vous vous en remettez essentiellement à la convention selon laquelle le même attribut aura toujours la même signification.

alors, comment renvoie-t-on plusieurs valeurs en Python?

841
demandé sur Community 2008-12-10 04:55:50

14 réponses

tuples nommés ont été ajoutés au point 2.6 à cette fin. Voir aussi os.stat pour un même builtin exemple.

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

dans les versions récentes de Python 3 (3.6+, je pense), la nouvelle bibliothèque typing a obtenu la classe NamedTuple pour rendre les tuples nommés plus faciles à créer et plus puissants. Hériter de typing.NamedTuple vous permet d'utiliser des docstrings, des valeurs par défaut, et des annotations de type.

exemple (tiré du docs):

class Employee(NamedTuple):  # inherit from collections.NamedTuple
    name: str
    id: int = 3  # default value

employee = Employee('Guido')
assert employee.id == 3
533
répondu A. Coady 2018-10-03 15:38:51

pour les petits projets je trouve plus facile de travailler avec tuples. Quand cela devient trop difficile à gérer (et pas avant) je commence à regrouper les choses dans des structures logiques, cependant je pense que votre utilisation suggérée de dictionnaires et d'objets ReturnValue est erronée (ou trop simpliste).

retourner un dictionnaire avec les clés y0, y1, y2 etc n'offre aucun avantage sur tuples. Retour d'une instance ReturnValue avec propriétés .y0 .y1 .y2 etc n'offre aucun avantage sur tuples soit. Vous devez commencer à nommer les choses si vous voulez obtenir n'importe où, et vous pouvez le faire en utilisant tuples de toute façon:

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

IMHO, la seule bonne technique au-delà de tuples est de retourner les objets réels avec les méthodes et les propriétés appropriées, comme vous obtenez de re.match() ou open(file) .

172
répondu too much php 2008-12-10 02:22:28

beaucoup de réponses suggèrent que vous devez retourner une collection d'une sorte, comme un dictionnaire ou une liste. Vous pouvez laisser de côté la syntaxe supplémentaire et simplement écrire les valeurs de retour, séparées par des virgules. Note: Ceci retourne techniquement un tuple.

def f():
    return True, False
x, y = f()
print(x)
print(y)

donne:

True
False
140
répondu Joseph Hansen 2018-04-03 15:44:20

je vote pour le dictionnaire.

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

aussi, l'introduction d'une structure "spéciale" rend votre code plus difficile à suivre. (Quelqu'un d'autre est à rechercher dans le code pour savoir ce que c'est)

si vous êtes préoccupé par le type rechercher, utiliser descriptif clés de dictionnaire, par exemple, "x-liste des valeurs'.

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0':y0, 'y1':y1 ,'y2':y2 }
57
répondu monkut 2008-12-10 02:42:05

une autre option serait d'utiliser des générateurs:

>>> def f(x):
        y0 = x + 1
        yield y0
        yield x * 3
        yield y0 ** 4


>>> a, b, c = f(5)
>>> a
6
>>> b
15
>>> c
1296

bien que les tuples IMHO soient généralement les meilleurs, sauf dans les cas où les valeurs retournées sont des candidats pour l'encapsulation dans une classe.

32
répondu rlms 2014-02-23 15:26:30

je préfère utiliser des tuples chaque fois qu'un tuple se sent" naturel"; les coordonnées sont un exemple typique, où les objets séparés peuvent se tenir sur leur propre, par exemple dans les calculs d'échelle à un seul axe, et l'ordre est important. Note: si je peux trier ou mélanger les articles sans effet négatif sur la signification du groupe, alors je ne devrais probablement pas utiliser un tuple.

j'utilise les dictionnaires comme valeur de retour seulement lorsque les objets groupés ne sont pas toujours les mêmes. Pensez facultatif d'e-mails.

pour le reste des cas, où les objets groupés ont un sens inhérent à l'intérieur du groupe ou un objet à part entière avec ses propres méthodes est nécessaire, j'utilise une classe.

24
répondu tzot 2018-05-30 16:53:38

je préfère

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

il semble que tout le reste soit un code supplémentaire pour faire la même chose.

22
répondu UnkwnTech 2008-12-10 02:00:49
>>> def func():
...    return [1,2,3]
...
>>> a,b,c = func()
>>> a
1
>>> b
2
>>> c
3
22
répondu WebQube 2015-01-21 20:57:30

en général, la" structure spécialisée " est en fait un état courant sensible d'un objet, avec ses propres méthodes.

class Some3SpaceThing(object):
  def __init__(self,x):
    self.g(x)
  def g(self,x):
    self.y0 = x + 1
    self.y1 = x * 3
    self.y2 = y0 ** y3

r = Some3SpaceThing( x )
r.y0
r.y1
r.y2

j'aime trouver des noms pour des structures anonymes quand c'est possible. Les noms significatifs rendent les choses plus claires.

14
répondu S.Lott 2008-12-10 02:33:16

+1 sur S. Lott la suggestion d'un nom de classe de conteneur.

pour python 2.6 et plus, un nommé tuple fournit un moyen utile de créer facilement ces classes de conteneurs, et les résultats sont "léger et ne nécessitent pas plus de mémoire que les tuples réguliers".

14
répondu John Fouhy 2008-12-10 03:51:47

les tuples, dicts et objets de Python offrent au programmeur un compromis facile entre la formalité et la commodité pour les petites structures de données ("choses"). Pour moi, le choix de la façon de représenter une chose est dicté principalement par la façon dont je vais utiliser la structure. En C++, c'est une convention courante d'utiliser struct pour les données seulement et class pour les objets avec des méthodes, même si vous pouvez légalement mettre des méthodes sur un struct ; mon habitude est similaire en Python, avec dict et tuple à la place de struct .

pour les ensembles de coordonnées, je vais utiliser un tuple plutôt qu'un point class ou un dict (et notez que vous pouvez utiliser un tuple comme une clé de dictionnaire, de sorte que dict s font de grands tableaux multidimensionnels épars).

si je dois itérer sur une liste de choses, je préfère déballer tuple s sur l'itération:

for score,id,name in scoreAllTheThings():
    if score > goodScoreThreshold:
        print "%6.3f #%6d %s"%(score,id,name)

... comme la version objet est plus encombré pour lire:

for entry in scoreAllTheThings():
    if entry.score > goodScoreThreshold:
        print "%6.3f #%6d %s"%(entry.score,entry.id,entry.name)

...sans parler du dict .

for entry in scoreAllTheThings():
    if entry['score'] > goodScoreThreshold:
        print "%6.3f #%6d %s"%(entry['score'],entry['id'],entry['name'])

si la chose est largement utilisée, et que vous vous trouvez à effectuer des opérations non-triviales similaires sur elle à plusieurs endroits dans le code, alors il est généralement utile d'en faire un objet de classe avec des méthodes appropriées.

enfin, si je dois échanger des données avec des composants du système non-Python, je les garderai le plus souvent dans un dict parce que c'est le mieux adapté à la sérialisation JSON.

14
répondu Russell Borogove 2013-08-13 20:18:55

dans des langues comme Python, j'utiliserais généralement un dictionnaire car il implique moins de frais généraux que la création d'une nouvelle classe.

cependant, si je me retrouve constamment à retourner le même ensemble de variables, alors cela implique probablement une nouvelle classe que je vais prendre en compte.

3
répondu fluffels 2008-12-10 02:02:37

j'utiliserais un dict pour passer et retourner les valeurs d'une fonction:

utiliser la forme variable définie dans la forme .

form = {
    'level': 0,
    'points': 0,
    'game': {
        'name': ''
    }
}


def test(form):
    form['game']['name'] = 'My game!'
    form['level'] = 2

    return form

>>> print(test(form))
{u'game': {u'name': u'My game!'}, u'points': 0, u'level': 2}

C'est le moyen le plus efficace pour moi et pour l'unité de traitement.

Vous avez passer juste un pointeur et de renvoyer simplement un pointeur.

vous n'avez pas à changer les fonctions ' (des milliers d'entre eux) arguments chaque fois que vous faites un changement dans votre code.

3
répondu Elis Byberi 2017-10-30 14:06:45

"le Meilleur" est partielle d'une décision subjective. Utilisez tuples pour les petits ensembles de retour dans le cas général où un immuable est acceptable. Un tuple est toujours préférable à une liste lorsque la mutabilité n'est pas une exigence.

pour les valeurs de retour plus complexes, ou pour le cas où la formalité est valable (c.-à-d. code de haute valeur) un tuple nommé est mieux. Pour le cas le plus complexe un objet est généralement préférable. Cependant, c'est la situation qui importe. Si cela a du sens retourner un objet parce que c'est ce que vous avez naturellement à la fin de la fonction (par exemple dessin D'usine) puis retourner l'objet.

comme le dit le sage:

l'optimisation prématurée est la racine de tout le mal (ou du moins la plupart des c') dans la programmation.

1
répondu joel3000 2018-08-29 19:30:23