Quel style utiliser pour les paramètres de retour inutilisés dans un appel de fonction Python

30

Existe-t-il un style de codage recommandé / généralement accepté pour gérer les situations où une fonction retourne un tuple de valeurs mais une seule de ces valeurs est utilisée par la suite (notez que cela est principalement destiné aux fonctions de bibliothèque que je ne peux pas changer - écrire un wrapper autour l'appel est probablement un peu exagéré…)? Au lieu de faire

a, b, c = foo()

et ensuite ne pas utiliser band c, laquelle des variantes suivantes devrait être préférée (ou y en a-t-il une autre?):

Variante 1 (soulignement)

a, _, _ = foo()

(qui est très clair et simple mais qui pourrait entrer en conflit avec _ = gettext.gettextde nombreuses applications utilisant la traduction)

Variante 2 (nom fictif)

a, unused, unused = foo()

(pas très attrayant, je pense, il en va de même pour d'autres noms comme dummy)

Variante 3 (index)

a = foo()[0]

(pour moi les ()[0]looks non pythoniques…)

user49643
la source
Cela contraste avec mon ancienne réponse, que se passe-t-il avec la variante 3 lorsque vous voulez maintenant référencer 2/3 des valeurs de retour? J'ai supprimé ma réponse parce que j'ai réalisé que je ne connaissais pas suffisamment Python pour la justifier.
Craige
@Craige: Je n'ai pas vu votre réponse avant de la supprimer ... Demandez-vous si a, b = foo()[0:2]cela fonctionnerait? Si oui: oui, c'est le cas :)
user49643
C'était exactement ce que je demandais. Si cela fonctionne (comme vous l'avez dit), j'utiliserais la variante 3. Je rétablis ma réponse compte tenu de ces informations.
Craige
3
Soit dit en passant, ces jours-ci, vous pouvez utiliser la syntaxe plutôt belle pour le déballage «étendu» : a, *_ = foo()supprimera toutes les valeurs sauf la première.
Benjamin Hodgson
duplicate of stackoverflow.com/questions/431866/…
Trevor Boyd Smith

Réponses:

17

L'utilisation du trait de soulignement pour les variables inutilisées est certainement acceptable. Soyez averti cependant, dans certaines bases de code, ce n'est pas une option car cet identifiant est réservé en raccourci gettext. C'est l'objection la plus fréquente à ce style (bien que ce ne soit pas un problème pour la majorité pour autant que je puisse en juger). Je le recommanderais toujours et je l'utiliserais toujours moi-même.

Des noms comme dummyou unusedont tendance à m'énerver personnellement, et je ne les vois pas très souvent (en Python, c'est-à-dire que je connais une base de code Delphi qui utilise dummygénéreusement et qui s'est également infiltrée dans des scripts associés au programme en question). Je vous déconseille.

Il suffit également d'extraire un élément du tuple retourné. Cela évite également quelques tracas pour obtenir le bon nombre de valeurs inutilisées. Notez cependant qu'il a deux inconvénients potentiels:

  • Il ne souffle pas si le nombre de valeurs est différent de ce que vous attendez. Cela peut être utile pour détecter les mélanges et les fautes de frappe.
  • Cela ne fonctionne que lorsque la valeur de retour est une séquence (c'est principalement des tuples et des listes, mais restons généraux). Offhand, je connais une classe dans mon code (un vecteur 2D) qui est itérable et donne un nombre constant de valeurs (et peut donc être utilisé dans le déballage des affectations), mais n'est pas indexable.

la source
Je ne fait mention getText dans ma question :) Quoi qu'il en soit, j'ai accepté votre réponse - il semble qu'il n'y a pas de style unique accepté claire , mais votre réponse a soulevé des points intéressants.
user49643
Pour éviter les problèmes avec gettext (ou pour éviter des problèmes similaires dans l'interpréteur), vous pouvez utiliser des doubles traits de soulignement (alias dunders) au lieu du simple.
Zim
21

Pylint m'a donné l'habitude de le faire de cette façon:

widget, _parent, _children = f()

Autrement dit, les résultats inutilisés ont un nom descriptif préfixé par _. Pylint considère les sections locales préfixées par _ comme inutilisées et les variables globales ou attributs préfixées par _ comme privées.

Vincent Povirk
la source
3

Si dans tout votre projet, vous ne faites cela qu'une seule fois ou très rarement par la fonction particulière, j'utiliserais la variante 1 si vous savez gettextque ce module ne pose pas de problème, et sinon la variante 3.

D'un autre côté, si vous faisiez beaucoup cela - et surtout si chaque fois que vous voulez un sous-ensemble différent des valeurs de retour (faire un wrapper pour ne renvoyer que celles qui vous tiennent à cœur), il pourrait être avantageux d'écrire un wrapper qui place les résultats dans un tuple nommé , ou une instance d'une autre classe descriptive, qui vous permettrait de faire:

bar = foo()

Et puis travailler avec bar.a, bar.bet bar.c.

lvc
la source
2

Comme d'autres l'ont dit, le soulignement ( _) est la norme. Mais si le soulignement est utilisé pour les traductions, je pense que le double soulignement est la meilleure alternative.

var, __, value = "VAR=value".partition('=')

Mieux que ceux-ci:

var, unused, value = "VAR=value".partition('=')

var, unused_del, value = "VAR=value".partition('=')

var, _del, value = "VAR=value".partition('=')

Rick Mohr
la source
1

Je ne suis pas un programmeur Python, mais pour moi, la troisième variante est la plus logique.

Dans la variante 3, vous êtes absolument clair sur les valeurs qui vous intéressent. Dans les variantes 1 et 2, vous réattribuez les valeurs aux variables et, par conséquent, elles peuvent être utilisées. Vous les avez peut-être nommés obscurément, mais une mauvaise dénomination n'est pas vraiment une solution à un problème.

Outre la clarté, pourquoi voudriez-vous affecter des valeurs inutilisées à un emplacement en mémoire (comme dans les variantes 1 et 2)? Ce serait une mauvaise solution en termes de gestion de la mémoire.

Craige
la source
Si vous le mettez toujours sur la même variable, le problème de mémoire ne serait-il pas uniquement la taille d'une seule variable à un moment donné? Je ne pense pas que cela devrait être un problème.
Michael McQuade
-2

Voici une règle générale: si seulement 1 des valeurs retournées est utilisée, pourquoi ne pas simplement retourner cette 1 valeur? Dans le cas où le drapeau est appelé à partir de plusieurs endroits, conservez un drapeau pour le même et retournez les valeurs selon le drapeau.

MODIFIER:

Dans le cas où j'étais dans votre situation et que je n'avais pas écrit la fonction, je choisirais peut-être la variante 2. Je garderais les noms fictifs là-bas afin que les paramètres puissent être utilisés en cas de besoin. Découper une liste aura un peu de frais généraux. Vous pouvez peut-être épargner quelques octets pour recevoir les données indésirables.

c0da
la source
-1 Je suppose qu'il n'a pas défini la fonction et ne peut donc pas changer ce qu'elle retourne. Je pense qu'il fait référence au moment où la fonction renvoie 3 valeurs, mais il ne se soucie que d'environ 1 dans son cas d'utilisation actuel.
Craige
@Craige: Oui, je voulais dire des fonctions que je ne pouvais pas changer - j'ai ajouté une note à ma question pour clarifier cela.
user49643
Êtes-vous sûr des frais généraux? J'ai essayé un très simple test ipython: par %timeit a, _, _ = foo()rapport à ce %timeit a = foo()[0]qui a donné lieu à 123ns par rapport à 109ns, à savoir le découpage semble être effectivement plus rapide que la valeur du déballage. Ou aviez-vous une situation particulière en tête?
user49643
2
Un autre scénario plausible est que vous n'avez pas besoin de toutes les valeurs pour certains appels.
Keith Thompson