La manière canonique de renvoyer plusieurs valeurs dans les langues qui la prennent en charge est souvent le couplage .
Option: utiliser 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. Et si vous souhaitez renvoyer quatre ou cinq valeurs? Bien sûr, vous pouvez continuer à les multiplier, mais il est facile d'oublier quelle valeur est où. Il est également assez 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 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}
(Pour être clair, y0, y1 et y2 ne sont que des identifiants 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é cela dans le contexte de Python, mais je suis sûr que cela s'applique également à d'autres langages. En effet, si vous travailliez en C, cela pourrait très bien être votre seule option. Voici:
class ReturnValue:
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, ils { y0, y1, y2 }
finissent par être des entrées à l'intérieur __dict__
du ReturnValue
.
Il y a une fonctionnalité supplémentaire fournie par Python pour les petits objets, l' __slots__
attribut. 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
__slots__
déclaration 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 enregistré car il__dict__
n'est pas créé pour chaque instance.
Option: utilisation d'une classe de données (Python 3.7+)
En utilisant les nouvelles classes de données de Python 3.7, renvoyez une classe avec des méthodes spéciales, des saisies et d'autres outils utiles ajoutés automatiquement:
@dataclass
class Returnvalue:
y0: int
y1: float
y3: int
def total_cost(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return ReturnValue(y0, y1, y2)
Option: utiliser une liste
Une autre suggestion que j'avais ignoré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 entaché par l'exposition à Haskell, mais l'idée de listes de types mixtes m'a toujours fait mal à l'aise. Dans cet exemple particulier, la liste est de type non-mixte, mais elle pourrait l'être.
Pour autant que je sache, une liste utilisée de cette manière n'a vraiment rien à voir avec le tuple. La seule vraie différence entre les listes et les tuples en Python est que les listes sont mutables , contrairement aux tuples.
Personnellement, j'ai tendance à reprendre 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?
y3
, qui fera le même travail aussi.
y3
, mais à moins que Y3 est déclarée mondiale, ce céderaisNameError: global name 'y3' is not defined
peut - être juste utiliser3
?