élément - structure données python




Comment renvoyez-vous plusieurs valeurs en Python? (9)

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

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

La manière canonique de renvoyer plusieurs valeurs dans les langues qui le prennent en charge est souvent le tupling .

Option: Utilisation d'un tuple

Considérez cet exemple trivial:

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

Cependant, cela devient rapidement problématique à mesure que le nombre de valeurs renvoyées augmente. Que faire si vous voulez retourner quatre ou cinq valeurs? Bien sûr, vous pouvez continuer à les trier, mais il devient facile d'oublier quelle valeur est où. Il est également plutôt moche de les déballer où vous voulez les recevoir.

Option: Utiliser un dictionnaire

La prochaine étape logique semble être d'introduire une sorte de «notation d'enregistrement». En python, la manière évidente de faire ceci 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 }

(edit - Pour être clair, y0, y1 et y2 sont simplement des identificateurs abstraits Comme indiqué, en pratique, vous utiliseriez 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']

Option: Utiliser une classe

Cependant, il existe une autre option. Nous pourrions plutôt retourner une structure spécialisée. J'ai encadré ceci dans le contexte de Python, mais je suis sûr qu'il s'applique aussi à d'autres langues. En effet, si vous travailliez en C, cela pourrait très bien être votre seule option. Voici:

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 } finissent par être des entrées dans le __dict__ interne de la ReturnValue .

Il existe cependant une fonctionnalité supplémentaire fournie par Python 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

Dans le manuel de référence Python :

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 the Lizard:

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

C'est ma méthode la moins préférée. Je suppose que je suis corrompu par l'exposition à Haskell, mais l'idée de listes mixtes m'a toujours semblé inconfortable. Dans cet exemple particulier, la liste est de type «non mixte», mais il est concevable qu'elle le soit. Une liste utilisée de cette manière ne gagne rien en ce qui concerne le tuple autant que je peux dire. La seule vraie différence entre les listes et les tuples dans Python est que les listes sont mutable , alors que les tuples ne le sont pas. J'ai personnellement tendance à reporter les conventions de la programmation fonctionnelle: utiliser des listes pour n'importe quel nombre d'éléments du même type, et des tuples pour un nombre fixe d'éléments de types prédéterminés.

Question

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

En général, je me suis retrouvé dans la voie du dictionnaire car cela impliquait moins de travail d'installation. Du point de vue des types cependant, vous feriez mieux d'aller sur la route de la classe, car cela pourrait vous aider à éviter de confondre ce qu'un dictionnaire représente. D'un autre côté, il y en a dans la communauté Python qui considère que les interfaces implicites devraient être préférées aux interfaces explicites , à quel point le type de l'objet n'est vraiment pas pertinent, puisque vous vous basez sur la convention que le même attribut aura toujours le même sens.

Alors, comment -you- renvoyez-vous plusieurs valeurs dans Python?


Beaucoup de réponses suggèrent que vous devez retourner une collection quelconque, comme un dictionnaire ou une liste. Vous pouvez ignorer la syntaxe supplémentaire et simplement écrire les valeurs de retour, séparées par des virgules. Note: cela renvoie techniquement un tuple.

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

donne:

True
False

Généralement, la "structure spécialisée" est en réalité un état actuel 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 si possible. Les noms significatifs rendent les choses plus claires.


J'utiliserais un dict pour passer et renvoyer des valeurs d'une fonction:

Utilisez une forme variable telle que définie dans le form .

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 devez passer juste un pointeur et renvoyer juste un pointeur.

Vous n'avez pas besoin de modifier les arguments des milliers de fonctions lorsque vous modifiez votre code.


Je vote pour le dictionnaire.

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

En outre, 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 le type de recherche, utilisez des clés de dictionnaire descriptives, par exemple, 'liste de valeurs x'.

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

Les tuples, les dicts et les objets de Python offrent au programmeur un compromis fluide entre formalité et 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 ++, il est courant d'utiliser struct pour les éléments de données uniquement et class pour les objets avec des méthodes, même si vous pouvez légalement mettre des méthodes sur une struct ; mon habitude est similaire en Python, avec dict et tuple à la place de struct .

Pour les ensembles de coordonnées, j'utiliserai un tuple plutôt qu'une class points ou un dict (et notez que vous pouvez utiliser un tuple comme une clé de dictionnaire, donc les dict font de superbes tableaux multidimensionnels épars).

Si je vais faire une itération sur une liste de choses, je préfère déballer des tuple sur l'itération:

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

... comme la version de l'objet est plus encombré de lire:

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

... sans parler de la 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 en train de faire des opérations non-triviales similaires à plusieurs endroits dans le code, alors il vaut généralement la peine d'en faire un objet de classe avec des méthodes appropriées.

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


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 à mon humble avis sont généralement les meilleurs, sauf dans les cas où les valeurs retournées sont des candidats pour l'encapsulation dans une classe.


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 n'est que du code supplémentaire pour faire la même chose.


>>> def func():
...    return [1,2,3]
...
>>> a,b,c = func()
>>> a
1
>>> b
2
>>> c
3




return-value