Comment tester plusieurs variables par rapport à une valeur?

645

J'essaie de créer une fonction qui comparera plusieurs variables à un entier et produira une chaîne de trois lettres. Je me demandais s'il y avait un moyen de traduire cela en Python. Dites donc:

x = 0
y = 1
z = 3
mylist = []

if x or y or z == 0 :
    mylist.append("c")
if x or y or z == 1 :
    mylist.append("d")
if x or y or z == 2 :
    mylist.append("e")
if x or y or z == 3 : 
    mylist.append("f")

qui retournerait une liste de:

["c", "d", "f"]

Est-ce que quelque chose comme ça est possible?

user1877442
la source
5
utilisation 1dans (tuple)
2
Lorsque vous souhaitez évaluer une liste d'instructions d'une manière quelconque, vous pouvez utiliser les fonctions any/ all. Par exemple: all([1, 2, 3, 4, False])retournera False all([True, 1, 2, 3])retournera True any([False, 0, 0, False])reviendra False any([False, 0, True, False])retournera True
eddd
4
Cette question est une cible en double très populaire, mais je pense qu'elle n'est pas optimale à cette fin. La plupart des gens essaient de faire quelque chose comme if x == 0 or 1:, qui est bien sûr similaire à if x or y == 0:, mais qui pourrait néanmoins être un peu déroutant pour les débutants. Étant donné le volume de "Pourquoi ne x == 0 or 1travaille- t-il pas ?" questions, je préfère de loin utiliser cette question comme notre cible canonique en double pour ces questions.
Aran-Fey
1
Faites très attention lorsque vous comparez à des valeurs "falsey" comme 0, 0.0ou False. Vous pouvez facilement écrire un code incorrect qui donne la «bonne» réponse.
smci

Réponses:

850

Vous comprenez mal le fonctionnement des expressions booléennes; ils ne fonctionnent pas comme une phrase en anglais et devinez que vous parlez de la même comparaison pour tous les noms ici. Tu recherches:

if x == 1 or y == 1 or z == 1:

xet ysont par ailleurs évalués par eux-mêmes ( Falsesi 0, Truesinon).

Vous pouvez raccourcir cela en utilisant un test de confinement par rapport à un tuple :

if 1 in (x, y, z):

ou mieux encore:

if 1 in {x, y, z}:

en utilisant aset pour bénéficier du test d'appartenance à coût constant ( inprend un temps fixe quel que soit l'opérande de gauche).

Lorsque vous utilisez or, python voit chaque côté de l'opérateur comme des expressions distinctes . L'expression x or y == 1est d'abord traitée comme un test booléen x, puis si c'est faux, l'expression y == 1est testée.

Cela est dû à la priorité de l'opérateur . L' oropérateur a une priorité plus faible que le ==test, ce dernier est donc évalué en premier .

Cependant, même si ce n'était pas le cas et que l'expression x or y or z == 1était en fait interprétée comme telle (x or y or z) == 1, cela ne ferait toujours pas ce que vous attendez d'elle.

x or y or zévaluerait le premier argument qui est «véridique», par exemple non False, numérique 0 ou vide (voir les expressions booléennes pour plus de détails sur ce que Python considère faux dans un contexte booléen).

Donc, pour les valeurs x = 2; y = 1; z = 0, x or y or zse résoudrait en 2, car c'est la première valeur vraie dans les arguments. Ce 2 == 1serait le cas False, même s'il le y == 1serait True.

La même chose s'appliquerait à l'inverse; tester plusieurs valeurs par rapport à une seule variable; x == 1 or 2 or 3échouerait pour les mêmes raisons. Utilisez x == 1 or x == 2 or x == 3ou x in {1, 2, 3}.

Martijn Pieters
la source
116
Je ne serais pas si rapide à opter pour la setversion. Les tuples sont très bon marché à créer et à parcourir. Sur ma machine au moins, les tuples sont plus rapides que les ensembles tant que la taille du tuple est d'environ 4 à 8 éléments. Si vous devez numériser plus que cela, utilisez un ensemble, mais si vous recherchez un élément sur 2 à 4 possibilités, un tuple est encore plus rapide! Si vous pouvez prendre des dispositions pour le cas le plus susceptible d'être le premier dans le tuple, la victoire est encore plus grand: (mon test: timeit.timeit('0 in {seq}'.format(seq=tuple(range(9, -1, -1)))))
SingleNegationElimination
57
@dequestarmappartialsetattr: Dans Python 3.3 et versions ultérieures, l'ensemble est stocké en tant que constante, contournant complètement le temps de création, éliminant le temps de création. La création de tuples peut être bon marché car Python met en cache un paquet pour éviter la perte de mémoire, ce qui fait la plus grande différence avec les ensembles ici.
Martijn Pieters
13
@dequestarmappartialsetattr: Si vous chronométrez juste le test d'appartenance, pour les ensembles d'entiers et les tuples sont également rapides pour le scénario idéal; correspondant au premier élément. Après cela, les tuples perdent aux jeux.
Martijn Pieters
17
@MartijnPieters: L'utilisation de la setnotation littérale pour ce test n'est pas une économie à moins que le contenu du setlittéral ne soit également littéral, non? if 1 in {x, y, z}:ne peut pas mettre en cache la set, parce que x, yet zpourrait changer, de sorte que soit les besoins en solution pour construire un tupleou à setpartir de zéro, et je soupçonne ce que lookup économies que vous pourriez obtenir lors de la vérification d'adhésion seraient submergés par une plus grande setdate de création.
ShadowRanger
9
@ShadowRanger: oui, l'optimisation des judas (que ce soit pour in [...]ou in {...}) ne fonctionne que si le contenu de la liste ou de l'ensemble est également des littéraux immuables.
Martijn Pieters
96

Votre problème est plus facilement résolu avec une structure de dictionnaire comme:

x = 0
y = 1
z = 3
d = {0: 'c', 1:'d', 2:'e', 3:'f'}
mylist = [d[k] for k in [x, y, z]]
dansalmo
la source
21
Ou même d = "cdef"ce qui mène àMyList = ["cdef"[k] for k in [x, y, z]]
aragaer
9
oumap(lambda i: 'cdef'[i], [x, y, z])
dansalmo
3
@MJM l'ordre de sortie n'est pas déterminé par le dict, il est déterminé par l'ordre de la liste[x, y, z]
dansalmo
1
Mis à part la compréhension de la liste à laquelle je ne suis pas encore habitué, la plupart d'entre nous avaient le même réflexe: construire ce dict!
LoneWanderer
66

Comme l'a déclaré Martijn Pieters, le format correct et le plus rapide est:

if 1 in {x, y, z}:

En utilisant son conseil, vous auriez maintenant des instructions if séparées afin que Python lise chaque instruction, que les premières soient Trueou False. Tel que:

if 0 in {x, y, z}:
    mylist.append("c")
if 1 in {x, y, z}:
    mylist.append("d")
if 2 in {x, y, z}:
    mylist.append("e")
...

Cela fonctionnera, mais si vous êtes à l'aise avec les dictionnaires (voir ce que j'ai fait là-bas), vous pouvez nettoyer cela en créant un dictionnaire initial mappant les nombres aux lettres que vous voulez, puis en utilisant simplement une boucle for:

num_to_letters = {0: "c", 1: "d", 2: "e", 3: "f"}
for number in num_to_letters:
    if number in {x, y, z}:
        mylist.append(num_to_letters[number])
ThatGuyRussell
la source
45

La façon directe d'écrire x or y or z == 0est

if any(map((lambda value: value == 0), (x,y,z))):
    pass # write your logic.

Mais je ne pense pas, vous l'aimez. :) Et cette façon est moche.

L'autre façon (une meilleure) est:

0 in (x, y, z)

BTW beaucoup de ifs pourraient être écrits comme quelque chose comme ça

my_cases = {
    0: Mylist.append("c"),
    1: Mylist.append("d")
    # ..
}

for key in my_cases:
    if key in (x,y,z):
        my_cases[key]()
        break
akaRem
la source
8
Dans votre exemple de au dictlieu d'une clé, vous obtiendrez des erreurs car la valeur de retour de .appendest None, et l'appel Nonedonne un AttributeError. En général, je suis d'accord avec cette méthode.
SethMMorton
2
le dict au lieu d'une clé est faux, vous obtiendrez Mylist = ['c', 'd'] lorsque le dictionnaire sera initialisé même si vous avez commenté la partie "for..loop"
Mahmoud Elshahat
1
Dans votre premier exemple, ce filterserait mieux que cela map, car il ne retournera que les cas où lambda est évalué comme vrai
Alex
1
Une compréhension est beaucoup plus simple qu'une carte d'un lambda:any(v == 0 for v in (x, y, z))
wjandrea
35

Si vous êtes très très paresseux, vous pouvez mettre les valeurs dans un tableau. Tel que

list = []
list.append(x)
list.append(y)
list.append(z)
nums = [add numbers here]
letters = [add corresponding letters here]
for index in range(len(nums)):
    for obj in list:
        if obj == num[index]:
            MyList.append(letters[index])
            break

Vous pouvez également mettre les chiffres et les lettres dans un dictionnaire et le faire, mais c'est probablement beaucoup plus compliqué que les simples instructions if. C'est ce que vous obtenez en essayant d'être très paresseux :)

Encore une chose, votre

if x or y or z == 0:

va compiler, mais pas de la façon dont vous le souhaitez. Lorsque vous mettez simplement une variable dans une instruction if (exemple)

if b

le programme vérifiera si la variable n'est pas nulle. Une autre façon d'écrire la déclaration ci-dessus (qui a plus de sens) est

if bool(b)

Bool est une fonction intégrée en python qui effectue essentiellement la commande de vérification d'une instruction booléenne (si vous ne savez pas ce que c'est, c'est ce que vous essayez de faire dans votre instruction if en ce moment :))

Une autre façon paresseuse que j'ai trouvée est:

if any([x==0, y==0, z==0])
ytpillai
la source
3
-1 Il y a beaucoup de mauvaises pratiques ici. listest un module intégré Python; utilisez un autre nom à la place, comme xyzpar exemple. Pourquoi construisez-vous la liste en quatre étapes alors que vous pouvez en faire une, c'est xyz = [x, y, z]-à- dire ? N'utilisez pas de listes parallèles, utilisez plutôt un dict. Dans l'ensemble, cette solution est beaucoup plus compliquée que celle de ThatGuyRussell . Aussi pour la dernière partie, pourquoi ne pas faire de compréhension, c'est à dire any(v == 0 for v in (x, y, z))? Les tableaux sont également quelque chose d'autre en Python.
wjandrea
32

Pour vérifier si une valeur est contenue dans un ensemble de variables, vous pouvez utiliser les modules intégrés itertoolset operator.

Par exemple:

Importations:

from itertools import repeat
from operator import contains

Déclarez les variables:

x = 0
y = 1
z = 3

Créez un mappage de valeurs (dans l'ordre que vous souhaitez vérifier):

check_values = (0, 1, 3)

Utilisez itertoolspour permettre la répétition des variables:

check_vars = repeat((x, y, z))

Enfin, utilisez la mapfonction pour créer un itérateur:

checker = map(contains, check_vars, check_values)

Ensuite, lors de la vérification des valeurs (dans l'ordre d'origine), utilisez next():

if next(checker)  # Checks for 0
    # Do something
    pass
elif next(checker)  # Checks for 1
    # Do something
    pass

etc...

Cela a un avantage sur le lambda x: x in (variables)car operatorest un module intégré et est plus rapide et plus efficace que l'utilisation lambdaqui doit créer une fonction sur place personnalisée.

Une autre option pour vérifier s'il y a une valeur non nulle (ou fausse) dans une liste:

not (x and y and z)

Équivalent:

not all((x, y, z))
GuiltyDolphin
la source
2
Cela ne répond pas à la question du PO. Il ne couvre que le premier cas dans l'exemple fourni.
wallacer
31

Définir est la bonne approche ici, car il ordonne les variables, ce qui semble être votre objectif ici. {z,y,x}est {0,1,3}quel que soit l'ordre des paramètres.

>>> ["cdef"[i] for i in {z,x,y}]
['c', 'd', 'f']

De cette façon, toute la solution est O (n).

BM
la source
5
Vous devez ajouter une description de ce que votre code accomplit et comment il le fait. Les réponses courtes utilisant uniquement du code sont déconseillées
Raniz
31

Toutes les excellentes réponses fournies ici se concentrent sur l'exigence spécifique de l'affiche originale et se concentrent sur la if 1 in {x,y,z}solution proposée par Martijn Pieters.
Ce qu'ils ignorent, c'est l'implication plus large de la question:
comment tester une variable par rapport à plusieurs valeurs?
La solution fournie ne fonctionnera pas pour les hits partiels si vous utilisez des chaînes par exemple:
Testez si la chaîne "Wild" est dans plusieurs valeurs

>>> x = "Wild things"
>>> y = "throttle it back"
>>> z = "in the beginning"
>>> if "Wild" in {x, y, z}: print (True)
... 

ou

>>> x = "Wild things"
>>> y = "throttle it back"
>>> z = "in the beginning"
>>> if "Wild" in [x, y, z]: print (True)
... 

pour ce scénario, il est plus facile de convertir en chaîne

>>> [x, y, z]
['Wild things', 'throttle it back', 'in the beginning']
>>> {x, y, z}
{'in the beginning', 'throttle it back', 'Wild things'}
>>> 

>>> if "Wild" in str([x, y, z]): print (True)
... 
True
>>> if "Wild" in str({x, y, z}): print (True)
... 
True

Il convient de noter cependant, comme mentionné par @codeforester, que les limites de mots sont perdues avec cette méthode, comme dans:

>>> x=['Wild things', 'throttle it back', 'in the beginning']
>>> if "rot" in str(x): print(True)
... 
True

les 3 lettres rotexistent en combinaison dans la liste mais pas en tant que mot individuel. Le test de «pourriture» échouerait, mais si l'un des éléments de la liste était «pourriture en enfer», cela échouerait également.
Le résultat étant, soyez prudent avec vos critères de recherche si vous utilisez cette méthode et sachez qu'elle a cette limitation.

Rolf de Saxe
la source
30

Je pense que cela va mieux gérer:

my_dict = {0: "c", 1: "d", 2: "e", 3: "f"}

def validate(x, y, z):
    for ele in [x, y, z]:
        if ele in my_dict.keys():
            return my_dict[ele]

Production:

print validate(0, 8, 9)
c
print validate(9, 8, 9)
None
print validate(9, 8, 2)
e
Bhargav Boda
la source
30

Si vous souhaitez utiliser les instructions if, else, voici une autre solution:

myList = []
aList = [0, 1, 3]

for l in aList:
    if l==0: myList.append('c')
    elif l==1: myList.append('d')
    elif l==2: myList.append('e')
    elif l==3: myList.append('f')

print(myList)
hamid
la source
26
d = {0:'c', 1:'d', 2:'e', 3: 'f'}
x, y, z = (0, 1, 3)
print [v for (k,v) in d.items() if x==k or y==k or z==k]
Saksham Varma
la source
26

Ce code peut être utile

L ={x, y, z}
T= ((0,"c"),(1,"d"),(2,"e"),(3,"f"),)
List2=[]
for t in T :
if t[0] in L :
    List2.append(t[1])
    break;
michael zxc858
la source
12

Vous pouvez essayer la méthode indiquée ci-dessous. Dans cette méthode, vous aurez la liberté de spécifier / saisir le nombre de variables que vous souhaitez saisir.

mydict = {0:"c", 1:"d", 2:"e", 3:"f"}
mylist= []

num_var = int(raw_input("How many variables? ")) #Enter 3 when asked for input.

for i in range(num_var): 
    ''' Enter 0 as first input, 1 as second input and 3 as third input.'''
    globals()['var'+str('i').zfill(3)] = int(raw_input("Enter an integer between 0 and 3 "))
    mylist += mydict[globals()['var'+str('i').zfill(3)]]

print mylist
>>> ['c', 'd', 'f']
Satpathie de Siddharth
la source
10

Solution en une ligne:

mylist = [{0: 'c', 1: 'd', 2: 'e', 3: 'f'}[i] for i in [0, 1, 2, 3] if i in (x, y, z)]

Ou:

mylist = ['cdef'[i] for i in range(4) if i in (x, y, z)]
Vinayak Kaniyarakkal
la source
9

Vous avez peut-être besoin d'une formule directe pour le jeu de bits de sortie.

x=0 or y=0 or z=0   is equivalent to x*y*z = 0

x=1 or y=1 or z=1   is equivalent to (x-1)*(y-1)*(z-1)=0

x=2 or y=2 or z=2   is equivalent to (x-2)*(y-2)*(z-2)=0

Mappons sur des bits: 'c':1 'd':0xb10 'e':0xb100 'f':0xb1000

Relation de isc (est «c»):

if xyz=0 then isc=1 else isc=0

Utilisez les mathématiques si la formule https://youtu.be/KAdKCgBGK0k?list=PLnI9xbPdZUAmUL8htSl6vToPQRRN3hhFp&t=315

[c]: (xyz=0 and isc=1) or (((xyz=0 and isc=1) or (isc=0)) and (isc=0))

[ré]: ((x-1)(y-1)(z-1)=0 and isc=2) or (((xyz=0 and isd=2) or (isc=0)) and (isc=0))

...

Connectez ces formules en suivant la logique:

  • la logique andest la somme des carrés des équations
  • la logique orest le produit d'équations

et vous aurez une somme expresse d'équation totale et vous avez une formule totale de somme

alors somme & 1 est c, somme & 2 est d, somme & 4 est e, somme & 5 est f

Après cela, vous pouvez former un tableau prédéfini où l'index des éléments de chaîne correspondrait à la chaîne prête.

array[sum] vous donne la chaîne.

Sergei
la source
7

Cela peut être fait facilement

for value in [var1,var2,var3]:
     li.append("targetValue")
Seenivasan
la source
6

La façon la plus mnémonique de représenter votre pseudo-code en Python serait:

x = 0
y = 1
z = 3
mylist = []

if any(v == 0 for v in (x, y, z)):
    mylist.append("c")
if any(v == 1 for v in (x, y, z)):
    mylist.append("d")
if any(v == 2 for v in (x, y, z)):
    mylist.append("e")
if any(v == 3 for v in (x, y, z)):
    mylist.append("f")
rsalmei
la source
1
Cette approche est plus universelle que `if 2 in (x, y, z): mylist.append ('e')` car permet des comparaisons arbitraires (par exemple if any(v >= 42 for v in (x, y, z)):). Et les performances des 3 méthodes ( 2 in {x,y,z}, 2 in (x,y,z), any(_v == 2 for _v in (x,y,z))) semble être à peu près la même chose dans CPython3.6 (voir Gist )
imposeren
5

Pour tester plusieurs variables avec une seule valeur: if 1 in {a,b,c}:

Pour tester plusieurs valeurs avec une variable: if a in {1, 2, 3}:

alamin
la source
4

On dirait que vous construisez une sorte de chiffre César.

Une approche beaucoup plus généralisée est la suivante:

input_values = (0, 1, 3)
origo = ord('c')
[chr(val + origo) for val in inputs]

les sorties

['c', 'd', 'f']

Je ne sais pas si c'est un effet secondaire souhaité de votre code, mais l'ordre de votre sortie sera toujours trié.

Si c'est ce que vous voulez, la dernière ligne peut être remplacée par:

sorted([chr(val + origo) for val in inputs])
firelynx
la source
2

Vous pouvez utiliser le dictionnaire:

x = 0
y = 1
z = 3
list=[]
dict = {0: 'c', 1: 'd', 2: 'e', 3: 'f'}
if x in dict:
    list.append(dict[x])
else:
    pass

if y in dict:
    list.append(dict[y])
else:
    pass
if z in dict:
    list.append(dict[z])
else:
    pass

print list
Rohit Gawas
la source
1
Cela peut même s'ajouter plus d'une fois. Ensemble?
Sergei
2

Sans dict, essayez cette solution:

x, y, z = 0, 1, 3    
offset = ord('c')
[chr(i + offset) for i in (x,y,z)]

et donne:

['c', 'd', 'f']
Massifox
la source
0

Cela vous aidera.

def test_fun(val):
    x = 0
    y = 1
    z = 2
    myList = []
    if val in (x, y, z) and val == 0:
        myList.append("C")
    if val in (x, y, z) and val == 1:
        myList.append("D")
    if val in (x, y, z) and val == 2:
        myList.append("E")

test_fun(2);
Mayur
la source
0

Vous pouvez unir cela

x = 0
y = 1
z = 3

dans une variable.

In [1]: xyz = (0,1,3,) 
In [2]: mylist = []

Modifiez nos conditions comme:

In [3]: if 0 in xyz: 
    ...:     mylist.append("c") 
    ...: if 1 in xyz: 
    ...:     mylist.append("d") 
    ...: if 2 in xyz: 
    ...:     mylist.append("e") 
    ...: if 3 in xyz:  
    ...:     mylist.append("f") 

Production:

In [21]: mylist                                                                                
Out[21]: ['c', 'd', 'f']
Serhii
la source
0

Problème

Alors que le modèle pour tester plusieurs valeurs

>>> 2 in {1, 2, 3}
True
>>> 5 in {1, 2, 3}
False

est très lisible et fonctionne dans de nombreuses situations, il y a un écueil:

>>> 0 in {True, False}
True

Mais nous voulons avoir

>>> (0 is True) or (0 is False)
False

Solution

Une généralisation de l'expression précédente est basée sur la réponse d' ytpillai :

>>> any([0 is True, 0 is False])
False

qui peut s'écrire

>>> any(0 is item for item in (True, False))
False

Bien que cette expression renvoie le bon résultat, elle n'est pas aussi lisible que la première expression :-(

fhgd
la source