Comment puis-je retourner plusieurs valeurs à partir d'une fonction?
La manière canonique de renvoyer plusieurs valeurs dans des langues que les soutenir, il est souvent tupling.
Option: à l'Aide d'un tuple
Considérer cet exemple trivial:
def f(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return (y0, y1, y2)
Cependant, cela devient vite problématique que le nombre de valeurs renvoyées augmente. Que faire si vous souhaitez revenir à quatre ou cinq valeurs? Bien sûr, vous pourriez garder tupling eux, mais il est facile d'oublier que la valeur est où. Il est également assez moche pour décompresser où vous souhaitez les recevoir.
Option: à l'Aide d'un dictionnaire
La prochaine étape logique semble être d'introduire une sorte de record de notation". En Python, le moyen le plus évident de le faire est par le biais d'un dict
.
De considérer les éléments suivants:
def g(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return {'y0': y0, 'y1': y1 ,'y2': y2}
(Juste pour être clair, y0, y1 et y2 sont juste comme des identifiants abstraits. Comme l'a fait remarquer, dans la pratique, vous devriez utiliser 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: à l'Aide d'une classe
Cependant, il ya une autre option. Nous avons pu, au lieu de 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. Va ici:
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édentes sont peut-être très similaires en termes de plomberie - après tout { y0, y1, y2 }
juste à la fin des entrées à l'intérieur __dict__
de la ReturnValue
.
Il y a une fonctionnalité supplémentaire fourni par Python, bien que pour de petits objets, la __slots__
attribut. La classe peut être exprimée comme:
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
__slots__
déclaration prend une séquence de variables d'instance et les réserves de juste assez d'espace dans chaque cas pour contenir une valeur pour chaque variable. L'espace est sauvé grâce à__dict__
n'est pas créé pour chaque instance.
Option: à l'Aide d'un dataclass (Python 3.7+)
À l'aide de Python 3.7 nouveau dataclasses, de retour d'une classe avec automatiquement ajoutés à des méthodes spéciales, de dactylographie et d'autres outils utiles:
@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: à l'Aide d'une liste
Une autre suggestion que j'avais négligé vient de Bill le Lézard:
def h(x):
result = [x + 1]
result.append(x * 3)
result.append(y0 ** y3)
return result
C'est au moins ma méthode préférée, cependant. Je suppose que je suis contaminé par l'exposition à Haskell, mais l'idée d'mixte-type de listes a toujours senti mal à l'aise pour moi. Dans cet exemple particulier, la liste n'est pas mixte, mais il pourrait, théoriquement, être.
Une liste utilisée de cette façon n'a vraiment pas de gagner quoi que ce soit avec le respect de la n-uplet d'aussi loin que je peux dire. La seule vraie différence entre les listes et les tuples en Python, c'est que les listes sont mutable, alors que les tuples ne 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 l'inévitable question. La méthode (pensez-vous) est le meilleur?
J'ai généralement trouvé moi-même d'aller le dictionnaire de la route, car elle implique moins de travail. À partir d'un des types de point de vue cependant, vous pourriez être mieux de passer par la classe de route, car qui peut vous aider à éviter la confusion entre ce dictionnaire représente.
D'autre part, il y en a dans la communauté Python qui se sentent implicite interfaces doivent être préférés à des interfaces explicites, à quel point le type de l'objet n'est pas vraiment pertinente, puisque vous êtes essentiellement en s'appuyant sur la convention que le même attribut ont toujours le même sens.
Alors, comment faites -vous - retourner plusieurs valeurs en Python?
- À vous d'excellents exemples vous utilisez une variable
y3
, mais à moins d'y3 est déclaré en global, cela donneraitNameError: global name 'y3' is not defined
peut-être juste utiliser3
? - Est en effet une question de l'attrait pour les opinions, mais le petit résumé sur le retour des valeurs est assez utile. Je vous remercie pour tous les Modérateurs et l'utilisateur qui a demandé
Vous devez vous connecter pour publier un commentaire.
Nommé tuples ont été ajoutés à 2.6 pour ce but. Voir aussi os.stat pour un même builtin exemple.
Dans les dernières versions de Python 3 (3.6+, je pense), la nouvelle
typing
bibliothèque eu laNamedTuple
classe nommée tuples plus facile de créer et de plus en plus puissants. Héritant detyping.NamedTuple
vous permet d'utiliser des docstrings, les valeurs par défaut, et les annotations de type.Exemple (à Partir de la documentation):
namedtuple
est d'avoir une plus petite empreinte mémoire pour masse résultats (une longue liste de tuples, tels que les résultats des requêtes DB). Pour les différents éléments (si la fonction en question ne s'appelle pas souvent), dictionnaires et les classes sont juste très bien ainsi. Mais namedtuples agréable/agréable solution dans ce cas.namedtuple
définitions (chaque appel crée un nouveau), de la création de lanamedtuple
classe est relativement cher dans les deux CPU et de la mémoire, et toutes les définitions de classe intrinsèquement impliquer cyclique références (ainsi de suite Disponible, vous êtes en attente pour une cyclique GC exécuter pour eux d'être libéré). Il rend également impossible lapickle
la classe et, par conséquent, impossible d'utiliser des instances avecmultiprocessing
dans la plupart des cas). Chaque création de la catégorie sur mon 3.6.4 x64 consomme ~0.337 ms, et occupe un peu moins de 1 KO de mémoire, de tuer en instance d'épargne.namedtuple
les classes; le CPU baisse des coûts d'environ un facteur de 4x, mais ils sont encore environ 1000x plus élevé que le coût pour créer une instance, et la mémoire des coûts pour chaque classe reste élevé (je me suis trompé dans mon dernier commentaire à propos de "moins de 1 KO" pour la classe,_source
par elle-même est généralement de 1,5 KB;_source
est retiré en 3.7, il est donc probablement plus proche de l'original de la demande d'un peu moins de 1 KO par la création de classes).import collections
. C'est pourquoi je continue à l'aide de dictionnaires.Pour les petits projets que j'ai trouver qu'il est plus facile de travailler avec des n-uplets. Quand cela devient trop difficile à gérer (et pas avant) je commence à grouper les choses dans des structures logiques, cependant, je pense que votre suggestion de l'utilisation de dictionnaires et de ReturnValue objets est mal (ou trop simpliste).
Retour d'un dictionnaire avec des touches de y0, y1, y2, etc. n'offre aucun avantage par rapport à n-uplets. Retour d'un ReturnValue exemple avec des propriétés .y0, .y1, .y2, etc. n'offre aucun avantage sur les tuples soit. Vous devez commencer à appeler les choses si vous voulez aller n'importe où, et vous pouvez le faire à l'aide des n-uplets de toute façon:
À mon humble avis, la seule bonne technique au-delà de n-uplets est de retour réel des objets avec des méthodes appropriées et des propriétés, comme vous obtenez à partir de
re.match()
ouopen(file)
.size, type, dimensions = getImageData(x)
et(size, type, dimensions) = getImageData(x)
? I. e., n'emballage de gauche côté d'un tupled affectation faire une différence?(1)
est un int alors que(1,)
ou1,
est un n-uplet.Un grand nombre de réponses suggèrent, vous devez retourner une collection d'une certaine sorte, comme un dictionnaire ou une liste. Vous pourriez laisser de côté l'extra syntaxe et il suffit d'écrire les valeurs de retour, séparées par des virgules. Remarque: cette technique renvoie un tuple.
donne:
type(f())
retourne<class 'tuple'>
.tuple
aspect explicite; il n'est pas vraiment important que vous êtes de retour d'unetuple
, c'est l'idiome pour retourner des valeurs multiples de la période. Même raison que vous omettre les parenthèses avec le swap idiome,x, y = y, x
, plusieurs d'initialisationx, y = 0, 1
, etc.; bien sûr, il faittuple
s sous le capot, mais il n'y a pas de raison de rendre explicite, puisque letuple
s ne sont pas au point du tout. Le tutoriel Python introduit l'affectation multiple longtemps avant qu'il ne touche même surtuple
s.=
est un tuple en Python, avec ou sans parenthèses autour d'eux. Donc il n'y a effectivement pas de manière explicite ou implicite ici. a,b,c est autant un tuple (a,b,c). Il ya aussi pas de faire de tuple "sous le capot" lorsque vous revenez de telles valeurs, parce que c'est juste une plaine simple d'un tuple. L'OP a déjà mentionné les tuples donc il n'y a en fait pas de différence entre ce qu'il a mentionné et que cette réponse montre. AucunJe vote pour le dictionnaire.
Je trouve que si je fais une fonction qui renvoie à quelque chose de plus que 2-3 variables, je vais le plier dans un dictionnaire. Sinon, j'ai tendance à oublier l'ordre et le contenu de ce que je suis de retour.
Aussi, l'introduction d'un "spécial" de la structure rend votre code plus difficile à suivre. (Quelqu'un d'autre est à rechercher dans le code pour savoir ce que c'est)
Si votre préoccupé par type rechercher, utiliser descriptif clés de dictionnaire, par exemple, "x-liste des valeurs'.
result = g(x); other_function(result)
Une autre option serait d'utiliser des générateurs:
Bien que à mon humble avis, les tuples sont généralement les mieux, sauf dans les cas où les valeurs retournées sont des candidats pour l'encapsulation dans une classe.
yield
?def f(x): …; yield b; yield a; yield r
vs(g for g in [b, a, r])
, et les deux seront facilement convertir à des listes ou des tuples, et le soutien tuple déballage. Le tuple générateur de la forme suit une approche fonctionnelle, tandis que la fonction est impératif et permettra le contrôle de flux et d'affectation de variable.Je préfère utiliser les tuples à chaque fois qu'un tuple se sent "naturels"; les coordonnées sont un exemple typique, où les objets distincts peuvent se tenir debout sur leurs propres, par exemple dans un axe seulement mise à l'échelle des calculs, et l'ordre est important. Remarque: si je peux tri ou de lecture aléatoire les éléments sans effet défavorable sur le sens du groupe, alors je ne devrais pas utiliser un tuple.
- Je utiliser les dictionnaires comme un retour de la valeur seulement quand les objets groupés ne sont pas toujours les mêmes. Pensez option d'e-mails.
Pour le reste des cas, où les objets groupés inhérentes à la signification à l'intérieur du groupe ou de l'une à part entière de l'objet avec ses propres méthodes est nécessaire, j'utilise une classe.
Que je préfère:
Il semble que tout le reste est juste un code supplémentaire de faire la même chose.
Généralement, la "structure spécialisée" en fait, EST un bon courant de l'état d'un objet, avec ses propres méthodes.
J'aime trouver des noms pour les structures où possible. Des noms significatifs rendre les choses plus claires.
Python tuples, les dicts, et des objets à offrir au programmeur une lisse compromis entre formalité et de commodité pour les petites structures de données ("choses"). Pour moi, le choix de la manière de représenter une chose est dictée principalement par la façon dont je vais me servir de la structure. En C++, c'est une convention commune à l'utilisation
struct
de données articles etclass
pour les objets avec des méthodes, même si vous pouvez légalement mis des méthodes sur unstruct
; mon habitude est similaire en Python, avecdict
ettuple
en place destruct
.Pour coordonner les séries, je vais utiliser un
tuple
plutôt que d'un point declass
ou undict
(et notez que vous pouvez utiliser untuple
comme une clé de dictionnaire, de sortedict
s faire de grands éparses tableaux multidimensionnels).Si je vais être itérer sur une liste de choses, je préfère le déballage
tuple
s sur l'itération:...comme la version de l'objet est plus encombré de lire:
...sans parler de la
dict
.Si la chose est largement utilisé, et vous vous retrouver à faire similaire non négligeable des opérations dans plusieurs endroits dans le code, alors que c'est généralement la peine d'en faire un objet de classe avec des méthodes appropriées.
Enfin, si je vais à l'échange de données avec les non-Python composants du système, je vais le plus souvent de les garder dans un
dict
parce que c'est le mieux adapté à la sérialisation JSON.+1 sur S. Lott la suggestion d'un nom de classe de conteneur.
Pour Python 2.6 et jusqu', un nommé tuple fournit un moyen utile de créer facilement ces classes conteneur, et les résultats sont "légers et ne nécessitent pas plus de mémoire que les tuples".
Dans des langages comme Python, j'avais l'habitude de se servir d'un dictionnaire, car elle implique moins de frais généraux que la création d'une nouvelle classe.
Cependant, si je me trouve constamment retourner le même ensemble de variables, puis qui implique sans doute une nouvelle classe que je vais facteur.
Je voudrais utiliser une dict à passer et les valeurs de retour d'une fonction:
Utiliser la forme variable tel que défini dans formulaire.
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 en dehors.
Vous n'avez pas à modifier les fonctions' (en milliers d'entre eux) les arguments à chaque fois que vous effectuez un changement dans votre code.
test
permettra de modifier directement la valeur. A comparer avecdict.update
, qui ne retourne pas de valeur.form
ne fait pas de mal la lisibilité ni les performances. Dans le cas où vous avez besoin de reformaterform
, de la retourner [formulaire] est-assurez-vous que la dernièreform
est retourné parce que vous n'aurez pas à garder la trace des modifications à n'importe où."Meilleur" est partielle d'une décision subjective. L'utilisation des n-uplets pour les petites rendement fixe dans le cas général où immuable est acceptable. Un tuple est toujours préférable à une liste lors de la mutabilité n'est pas une exigence.
Plus complexes, les valeurs de retour, ou pour le cas où la formalité est utile (c'est à dire à haute valeur de code) un nommé n-uplet est mieux. Pour le cas le plus complexe d'un objet est souvent le meilleur. Cependant, c'est vraiment la situation que les questions. Si elle fait sens pour retourner un objet, car c'est ce que vous, naturellement, à la fin de la fonction (par exemple, modèle de Fabrique), puis retourner l'objet.
Que l'homme sage a dit: