Manière plus élégante de déclarer plusieurs variables en même temps

140

Pour déclarer plusieurs variables en «même temps», je ferais:

a, b = True, False

Mais si je devais déclarer beaucoup plus de variables, cela devient de moins en moins élégant:

a, b, c, d, e, f, g, h, i, j = True, True, True, True, True, False, True ,True , True, True

Existe-t-il un moyen meilleur / élégant / pratique de le faire?

Cela doit être très basique, mais si j'utilisais une liste ou un tuple pour stocker les variables, comment devrais-je m'approcher pour que je sois utile puisque:

aList = [a,b]

N'est pas valide, je devrais faire:

a, b = True, True

Ou qu'est-ce que je manque?

Trufa
la source
Utiliser une liste pour stocker ces valeurs? Un dictionnaire? Un tuple (nommé)?
Jeff Mercado
@Chris: J'y arrivais. :)
Jeff Mercado
@JeffM: peut-être mais je ne sais pas comment faire ça il semble qu'il faut les définir pour appartenir à une liste (je me trompe peut-être bien sûr)
Trufa
3
@Trufa: Si vous allez déclarer que de nombreuses variables pour stocker des valeurs, c'est déjà un signe que vous devriez envisager d'autres alternatives de stockage à mon humble avis.
Jeff Mercado
1
@ user470379 - J'ai en quelque sorte supposé que les noms étaient juste pour l'exemple de code, et que Trufa n'utilise pas ces noms dans son vrai code.
Chris Lutz

Réponses:

52

Comme d'autres l'ont suggéré, il est peu probable que l'utilisation de 10 variables locales différentes avec des valeurs booléennes soit la meilleure façon d'écrire votre routine (surtout si elles ont vraiment des noms à une lettre :)

Selon ce que vous faites, il peut être judicieux d'utiliser un dictionnaire à la place. Par exemple, si vous souhaitez configurer des valeurs booléennes prédéfinies pour un ensemble d'indicateurs à une lettre, vous pouvez le faire:

>>> flags = dict.fromkeys(["a", "b", "c"], True)
>>> flags.update(dict.fromkeys(["d", "e"], False))
>>> print flags
{'a': True, 'c': True, 'b': True, 'e': False, 'd': False}

Si vous préférez, vous pouvez également le faire avec une seule instruction d'affectation:

>>> flags = dict(dict.fromkeys(["a", "b", "c"], True),
...              **dict.fromkeys(["d", "e"], False))
>>> print flags
{'a': True, 'c': True, 'b': True, 'e': False, 'd': False}

Le deuxième paramètre dictn'est pas entièrement conçu pour cela: il est vraiment destiné à vous permettre de remplacer des éléments individuels du dictionnaire en utilisant des arguments de mots clés tels que d=False. Le code ci-dessus gonfle le résultat de l'expression suivante **dans un ensemble d' arguments de mot - clé qui sont passés à la fonction appelée. C'est certainement un moyen fiable de créer des dictionnaires, et les gens semblent au moins accepter cet idiome, mais je soupçonne que certains peuvent le considérer comme unpythonique. </disclaimer>


Une autre approche, qui est probablement la plus intuitive si vous utilisez fréquemment ce modèle, consiste à définir vos données sous la forme d'une liste de valeurs d'indicateur ( True, False) mappées à des noms d'indicateur (chaînes à un seul caractère). Vous transformez ensuite cette définition de données en un dictionnaire inversé qui mappe les noms d'indicateur aux valeurs d'indicateur. Cela peut être fait assez succinctement avec une compréhension de liste imbriquée, mais voici une implémentation très lisible:

>>> def invert_dict(inverted_dict):
...     elements = inverted_dict.iteritems()
...     for flag_value, flag_names in elements:
...         for flag_name in flag_names:
...             yield flag_name, flag_value
... 
>>> flags = {True: ["a", "b", "c"], False: ["d", "e"]}
>>> flags = dict(invert_dict(flags))
>>> print flags
{'a': True, 'c': True, 'b': True, 'e': False, 'd': False}

La fonction invert_dictest une fonction de générateur . Il génère ou produit - ce qui signifie qu'il renvoie à plusieurs reprises des valeurs de - paires clé-valeur. Ces paires clé-valeur sont l'inverse du contenu des deux éléments du flagsdictionnaire initial . Ils sont introduits dans le dictconstructeur. Dans ce cas, le dictconstructeur fonctionne différemment de ci-dessus car il est alimenté par un itérateur plutôt qu'un dictionnaire comme argument.


S'inspirant du commentaire de @Chris Lutz: si vous allez vraiment l'utiliser pour des valeurs à un seul caractère, vous pouvez en fait faire

>>> flags = {True: 'abc', False: 'de'}
>>> flags = dict(invert_dict(flags))
>>> print flags
{'a': True, 'c': True, 'b': True, 'e': False, 'd': False}

Cela fonctionne parce que les chaînes Python sont itérables , ce qui signifie qu'elles peuvent être déplacées valeur par valeur. Dans le cas d'une chaîne, les valeurs sont les caractères individuels de la chaîne. Ainsi, lorsqu'ils sont interprétés comme des itérables, comme dans ce cas où ils sont utilisés dans une boucle for, ['a', 'b', 'c']et 'abc'sont effectivement équivalents. Un autre exemple serait quand ils sont passés à une fonction qui prend un itérable, comme tuple.

Personnellement, je ne ferais pas cela parce que cela ne se lit pas intuitivement: quand je vois une chaîne, je m'attends à ce qu'elle soit utilisée comme une valeur unique plutôt que comme une liste. Alors je regarde la première ligne et je pense: "D'accord, il y a donc un vrai drapeau et un faux drapeau." Donc, bien que ce soit une possibilité, je ne pense pas que ce soit la voie à suivre. D'un autre côté, il peut être utile d'expliquer plus clairement les concepts d'itérables et d'itérateurs.


Définir la fonction de invert_dicttelle sorte qu'elle renvoie réellement un dictionnaire n'est pas non plus une mauvaise idée; La plupart du temps, je ne l'ai pas fait parce que cela n'aide pas vraiment à expliquer comment fonctionne la routine.


Apparemment, Python 2.7 a des compréhensions de dictionnaire, ce qui constituerait un moyen extrêmement concis d'implémenter cette fonction. Ceci est laissé comme exercice au lecteur, car je n'ai pas installé Python 2.7 :)

Vous pouvez également combiner certaines fonctions du module itertools toujours polyvalent . Comme on dit, il y a plus d'une façon de le faire . Attendez, les gens de Python ne disent pas ça. Eh bien, c'est vrai de toute façon dans certains cas. Je suppose que Guido nous a donné des compréhensions de dictionnaires afin qu'il y ait une façon évidente de faire cela.

intuité
la source
1
Notez que ['a', 'b', 'c']peut être raccourci à list('abc'), qui inspire def truth_values(trues, falses): d = dict.from_keys(list(trues), True); d.update(dict.from_keys(list(falses), False)); return dutilisé commevalues = truth_values("abc", "de")
Chris Lutz
Merci beaucoup, cela semble être une réponse très complète, je vais l'examiner attentivement et expérimenter avec ce que vous prez, ce que vous dites, même si c'est probablement vrai, cela ne me vient pas naturellement, alors je devrai lisez un peu et jouez jusqu'à ce que je comprenne pleinement ce que vous voulez dire, surtout depuis les dictionnaires qui sont encore l'une de mes faiblesses en python. Merci beaucoup, je reviendrai :)
Trufa
5
@intuited Je suis sidéré par votre réponse: vous définissez un autre problème que celui de l'OP et vous vous réjouissez de faire une longue réponse à cet autre problème. Il ne veut pas associer des chaînes et des valeurs dans un dictionnaire, il veut créer des objets avec un identifiant et une valeur pour chacun.
eyquem
5
@eyquem: Les longues réponses sont mauvaises? Médecin, guéris-toi toi-même!
John Machin
5
Cela ne répond pas à la question et c'est condescendant. Voir la réponse ci
kodu
225
a, b, c, d, e, g, h, i, j = (True,)*9
f = False
Shariq
la source
21
@Imray La notation de fin de virgule (d,)crée un tuple à un élément, qui est un type de séquence. Les types de séquence prennent en charge l'addition et la multiplication, entre autres opérations.
duozmo
36
Astuce élégante, mais soyez conscient de l'utiliser sur des éléments mutables tels que des listes. Par exemple a, b, c = ([],)*3ne crée pas 3 instances de liste mais marques a, bet cpointant vers la même instance.
Zac
5
Je me demande combien de temps je perds / passe à essayer de rendre mon code python uber pythonique?
SARose
3
@Zac pour le faire correctement, utilisez a, b, c = ([] for i in range(3)). source . Par souci de cohérence, vous pouvez également utiliser une variante de celle-ci pour cette réponse, c'est-à-dire a,b,c,d,e,g,h,i,j = (True for i in range(9)) f=(False i in range(1)).
Novice C
Ceci est un exemple de la raison pour laquelle j'aime python, Je ne l'utilise que récemment .. Je souhaite que ce soit commencé plus tôt .. Mais le développement Web varie en fonction du projet.
Angry 84
52

Utilisez une liste / un dictionnaire ou définissez votre propre classe pour encapsuler les éléments que vous définissez, mais si vous avez besoin de toutes ces variables, vous pouvez le faire:

a = b = c = d = e = g = h = i = j = True
f = False
yan
la source
1
vars ne sera pas défini uniquement sur Vrai et Faux.
N 1.1
1
@ N1.1: Je ne pouvais pas comprendre ce que vous vouliez dire par là.
Trufa
@Trufa S'ils sont définis sur True / False uniquement, la méthode suggérée est parfaite, mais que se passe-t-il si les variables sont définies sur des choses différentes?
N 1.1
@ N1.1: Ohh je comprends ce que tu veux dire, merci pour la clarification! Dans ce cas, ce sont tous des idiots mais c'est bon à savoir. Merci
Trufa
5
Pour une explication des raisons pour lesquelles il s'agit d'un modèle dangereux (bien que cela fonctionne), lisez Notorious BIG
duozmo
10

Ceci est une élaboration sur @ Jeff M et mes commentaires.

Lorsque vous faites ceci:

a, b = c, d

Cela fonctionne avec l'emballage et le déballage de tuple. Vous pouvez séparer les étapes d'emballage et de déballage:

_ = c, d
a, b = _

La première ligne crée un tuple appelé _qui a deux éléments, le premier avec la valeur de cet le second avec la valeur de d. La deuxième ligne décompresse le _tuple dans les variables aet b. Cela décompose votre unique ligne énorme:

a, b, c, d, e, f, g, h, i, j = True, True, True, True, True, False, True, True, True, True

En deux lignes plus petites:

_ = True, True, True, True, True, False, True, True, True, True
a, b, c, d, e, f, g, h, i, j = _

Cela vous donnera exactement le même résultat que la première ligne (y compris la même exception si vous ajoutez des valeurs ou des variables à une partie mais oubliez de mettre à jour l'autre). Cependant, dans ce cas précis, la réponse de yan est peut-être la meilleure.

Si vous avez une liste de valeurs, vous pouvez toujours les décompresser. Il vous suffit d'abord de le convertir en tuple. Par exemple, ce qui suit attribuera respectivement une valeur comprise entre 0 et 9 à chacun des athrough j:

a, b, c, d, e, f, g, h, i, j = tuple(range(10))

EDIT: astuce pour les attribuer tous comme vrais sauf l'élément 5 (variable f):

a, b, c, d, e, f, g, h, i, j = tuple(x != 5 for x in range(10))
Chris Lutz
la source
Je ne savais pas que le dernier était possible, je vais certainement essayer, c'est en effet un truc sympa et cela peut être utile!
Trufa
5

Lorsque les gens suggèrent "d'utiliser une liste, un tuple ou une autre structure de données", ils disent que, lorsque vous avez beaucoup de valeurs différentes qui vous intéressent, les nommer toutes séparément en tant que variables locales n'est peut-être pas la meilleure façon faire des choses.

Au lieu de cela, vous souhaiterez peut-être les rassembler dans une structure de données plus grande qui peut être stockée dans une seule variable locale.

intuited a montré comment vous pourriez utiliser un dictionnaire pour cela, et Chris Lutz a montré comment utiliser un tuple pour le stockage temporaire avant de le décompresser en variables séparées, mais une autre option à considérer est d'utiliser collections.namedtuplepour regrouper les valeurs de manière plus permanente.

Vous pourriez donc faire quelque chose comme:

# Define the attributes of our named tuple
from collections import namedtuple
DataHolder = namedtuple("DataHolder", "a b c d e f g")

# Store our data
data = DataHolder(True, True, True, True, True, False, True)

# Retrieve our data
print(data)
print(data.a, data.f)

Le vrai code utiliserait, espérons-le, des noms plus significatifs que "DataHolder" et les lettres de l'alphabet, bien sûr.

ncoghlan
la source
Merci pour votre réponse, je vais vérifier cela comme une option, le fait est que ( pour ce cas particulier ) il peut ne pas être utile d'avoir une structure immuable. Je commenterai plus tard comment cela s'est avéré, encore une fois merci beaucoup!
Trufa
Vous pouvez faire la même chose avec une classe ordinaire - la définition de DataHolderdevient juste un peu plus verbeuse.
ncoghlan
4

Quel est le problème, en fait?

Si vous avez vraiment besoin ou voulez vraiment 10 a , b , c , d , e , f , g , h , i , j , il n'y aura pas d'autre possibilité, à un moment ou à un autre, d'écrire a et d'écrire b et d'écrire c . ....

Si les valeurs sont toutes différentes, vous serez obligé d'écrire par exemple

a = 12
b= 'sun'
c = A() #(where A is a class)
d = range(1,102,5)
e = (line in filehandler if line.rstrip())
f = 0,12358
g = True
h = random.choice
i = re.compile('^(!=  ab).+?<span>')
j = [78,89,90,0]

c'est-à-dire définir les «variables» individuellement.

Ou, en utilisant une autre écriture, pas besoin d'utiliser _:

a,b,c,d,e,f,g,h,i,j =\
12,'sun',A(),range(1,102,5),\
(line for line in filehandler if line.rstrip()),\
0.12358,True,random.choice,\
re.compile('^(!=  ab).+?<span>'),[78,89,90,0]

ou

a,b,c,d,e,f,g,h,i,j =\
(12,'sun',A(),range(1,102,5),
 (line for line in filehandler if line.rstrip()),
 0.12358,True,random.choice,
 re.compile('^(!=  ab).+?<span>'),[78,89,90,0])

.

Si certains d'entre eux doivent avoir la même valeur, le problème est-il qu'il est trop long à écrire

a, b, c, d, e, f, g, h, i, j = True, True, True, True, True, False, True ,True , True, True 

?

Ensuite, vous pouvez écrire:

a=b=c=d=e=g=h=i=k=j=True
f = False

.

Je ne comprends pas quel est exactement votre problème. Si vous souhaitez écrire un code, vous êtes obligé d'utiliser les caractères requis par l'écriture des instructions et des définitions. Quoi d'autre ?

Je me demande si votre question n'est pas le signe que vous avez mal compris quelque chose.

Quand on écrit a = 10, on ne crée pas de variable au sens de "morceau de mémoire dont la valeur peut changer". Cette instruction:

  • soit déclenche la création d'un objet de type integeret valeur 10 et la liaison d'un nom 'a' avec cet objet dans l'espace de noms courant

  • ou réattribuer le nom 'a' dans l'espace de noms à l'objet 10 (car 'a' était précédemment lié à un autre objet)

Je dis ça parce que je ne vois pas l'utilité de définir 10 identifiants a, b, c ... pointant vers False ou True. Si ces valeurs ne changent pas lors de l'exécution, pourquoi 10 identifiants? Et s'ils changent, pourquoi définir les identifiants en premier?, Ils seront créés en cas de besoin s'ils ne sont pas définis au préalable

Ta question me paraît bizarre

eyquem
la source
2

On dirait que vous abordez votre problème de la mauvaise manière pour moi.

Réécrivez votre code pour utiliser un tuple ou écrivez une classe pour stocker toutes les données.

richo
la source
Merci pour vous pouvez-vous dire cela sans même un aperçu du code :) Je comprends que ce n'est pas idéal, et je devrai peut-être reformater mais votre réponse ne résout pas vraiment la question. Je comprends cependant votre point.
Trufa
1
Je peux dire que ça ressemble à ça, oui. C'est ce à quoi ça ressemble. La refactorisation résoudra probablement votre problème. Votre problème est un problème de style, pas fonctionnel, il est donc difficile de proposer autre chose que des métonseils.
richo
1

J'aime la réponse la plus votée; cependant, il a des problèmes avec la liste comme indiqué.

  >> a, b = ([0]*5,)*2
  >> print b
  [0, 0, 0, 0, 0]
  >> a[0] = 1
  >> print b
  [1, 0, 0, 0, 0]

Ceci est discuté en détail (ici) , mais l'essentiel est que aetb sont le même objet avec le a is bretour True(même pour id(a) == id(b)). Par conséquent, si vous modifiez un index, vous modifiez l'index des deux aet b, puisqu'ils sont liés. Pour résoudre ce problème, vous pouvez faire (source)

>> a, b = ([0]*5 for i in range(2))
>> print b
[0, 0, 0, 0, 0]
>> a[0] = 1
>> print b
[0, 0, 0, 0, 0]

Cela peut ensuite être utilisé comme une variante de la première réponse, qui a les résultats intuitifs «souhaités»

>> a, b, c, d, e, g, h, i = (True for i in range(9))
>> f = (False for i in range(1)) #to be pedantic
Novice C
la source
1

Dans votre cas, j'utiliserais YAML.

C'est une norme élégante et professionnelle pour traiter plusieurs paramètres. Les valeurs sont chargées à partir d'un fichier séparé. Vous pouvez voir quelques informations dans ce lien:

https://keleshev.com/yaml-quick-introduction

Mais il est plus facile de le rechercher sur Google, car c'est une norme, il y a des centaines d'informations à ce sujet, vous pouvez trouver ce qui correspond le mieux à votre compréhension. ;)

Meilleures salutations.

Henrique LR
la source
Salut Henrique, bienvenue chez SO, merci pour ta réponse! Assurez-vous de lire attentivement la rédaction d'une réponse avant de répondre à votre prochaine question!
Diggy.
0

Comme JavaScript, vous pouvez également utiliser plusieurs instructions sur une seule ligne en pythona = 1; b = "Hello World"; c += 3

Isaac Frost
la source