Come testare più variabili rispetto a un valore?


645

Sto cercando di creare una funzione che confronterà più variabili con un numero intero e produrrà una stringa di tre lettere. Mi chiedevo se ci fosse un modo per tradurre questo in Python. Allora, dici:

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")

che restituirebbe un elenco di:

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

È possibile qualcosa del genere?


5
use 1in (tuple)

2
Quando si desidera valutare un elenco di istruzioni in qualsiasi / tutti i modi, è possibile utilizzare any/ allfunzioni. Ad esempio: all([1, 2, 3, 4, False])restituirà False all([True, 1, 2, 3])restituirà True any([False, 0, 0, False])restituirà False any([False, 0, True, False])restituirà True
eddd

4
Questa domanda è un obiettivo duplicato molto popolare, ma penso che non sia ottimale a tale scopo. Molte persone cercano di fare qualcosa del genere if x == 0 or 1:, che è ovviamente simile if x or y == 0:, ma potrebbe essere un po 'di confusione per i neofiti. Dato il volume puro di "Perché non x == 0 or 1lavoro?" domande, preferirei di gran lunga usare questa domanda come nostro obiettivo canonico duplicato per queste domande.
Aran-Fey,

1
Prestare particolare attenzione quando si confrontano valori "falsi" come 0, 0.0o False. Puoi facilmente scrivere un codice sbagliato che dia la risposta "giusta".
smci,

Risposte:


850

Capisci male come funzionano le espressioni booleane; non funzionano come una frase inglese e immagino che tu stia parlando dello stesso confronto per tutti i nomi qui. Stai cercando:

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

xe ysono altrimenti valutati da soli ( Falsese 0, Truealtrimenti).

Puoi abbreviarlo usando un test di contenimento contro una tupla :

if 1 in (x, y, z):

o meglio ancora:

if 1 in {x, y, z}:

utilizzando aset per sfruttare il test di appartenenza a costo costante ( inrichiede un periodo di tempo fisso qualunque sia l'operando di sinistra).

Quando si utilizza or, Python vede ogni lato dell'operatore come espressioni separate . L'espressione x or y == 1viene considerata prima come un test booleano x, quindi se è falso, l'espressione y == 1viene testata.

Ciò è dovuto alla precedenza dell'operatore . L' oroperatore ha una precedenza inferiore rispetto al ==test, quindi quest'ultimo viene valutato per primo .

Tuttavia, anche se non fosse così, e l'espressione x or y or z == 1fosse effettivamente interpretata come (x or y or z) == 1invece, ciò non farebbe comunque ciò che ci si aspetta che faccia.

x or y or zvaluterebbe il primo argomento "verità", ad esempio no False, 0 numerico o vuoto (vedere le espressioni booleane per i dettagli su ciò che Python considera falso in un contesto booleano).

Quindi, per i valori x = 2; y = 1; z = 0, x or y or zsi risolverebbe 2, perché quello è il primo valore vero negli argomenti. Quindi 2 == 1sarebbe False, anche se y == 1sarebbe True.

Lo stesso si applicherebbe all'inverso; test di più valori su una singola variabile; x == 1 or 2 or 3fallirebbe per gli stessi motivi. Usa x == 1 or x == 2 or x == 3o x in {1, 2, 3}.


116
Non sarei così veloce per la setversione. Le Tuple sono molto economiche da creare e ripetere. Almeno sulla mia macchina, le tuple sono più veloci degli insiemi fintanto che la dimensione della tupla è di circa 4-8 elementi. Se devi scansionare più di questo, usa un set, ma se stai cercando un oggetto con 2-4 possibilità, una tupla è ancora più veloce! Se si può organizzare per il caso più probabile che sia prima nella tupla, la vittoria è ancora più grande: (il mio test: timeit.timeit('0 in {seq}'.format(seq=tuple(range(9, -1, -1)))))
SingleNegationElimination

57
@dequestarmappartialsetattr: in Python 3.3 e versioni successive, il set viene memorizzato come costante, ignorando del tutto il tempo di creazione, eliminando il tempo di creazione. Le tuple possono essere economiche da creare in quanto Python ne memorizza in cache un fascio per evitare l'abbandono della memoria, facendo la differenza più grande con i set qui.
Martijn Pieters

13
@dequestarmappartialsetattr: se cronometri solo il test di appartenenza, per interi e tuple sono ugualmente veloci per lo scenario ideale; abbinando il primo elemento. Dopodiché le tuple si perdono nei set.
Martijn Pieters

17
@MartijnPieters: l'uso della setnotazione letterale per questo test non è un risparmio a meno che il contenuto del setletterale non sia anche letterale, giusto? if 1 in {x, y, z}:non riesco a memorizzare nella cache il setperché x, ye zpotrebbe cambiare, quindi entrambe le soluzioni devono creare da zero tupleo setda zero, e sospetto che qualsiasi risparmio di ricerca che potresti ottenere quando verifichi l'adesione verrebbe sommerso da un settempo di creazione maggiore .
ShadowRanger

9
@ShadowRanger: sì, l'ottimizzazione dello spioncino (che sia per in [...]o in {...}) funziona solo se i contenuti dell'elenco o del set sono anche valori letterali immutabili.
Martijn Pieters

96

Il tuo problema viene risolto più facilmente con una struttura di dizionario come:

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

21
O addirittura d = "cdef"che porta aMyList = ["cdef"[k] for k in [x, y, z]]
aragaer il

9
oppuremap(lambda i: 'cdef'[i], [x, y, z])
dansalmo,

3
@MJM l'ordine di uscita non è determinato dal dict, è determinato dall'ordine della lista[x, y, z]
dansalmo

1
A parte la comprensione dell'elenco a cui non sono ancora completamente abituato, molti di noi hanno avuto lo stesso riflesso: costruisci quel dict!
LoneWanderer,

66

Come affermato da Martijn Pieters, il formato corretto e più veloce è:

if 1 in {x, y, z}:

Usando il suo consiglio ora avresti delle istruzioni if ​​separate in modo che Python leggerà ogni affermazione se la prima fosse Trueo False. Ad esempio:

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")
...

Funzionerà, ma se ti senti a tuo agio con i dizionari (vedi cosa ho fatto lì), puoi ripulirlo creando un dizionario iniziale che associa i numeri alle lettere che vuoi, quindi semplicemente usando un ciclo 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])

45

Il modo diretto di scrivere x or y or z == 0è

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

Ma non penso, ti piace. :) E in questo modo è brutto.

L'altro modo (un migliore) è:

0 in (x, y, z)

A proposito un sacco di ifs potrebbe essere scritto come qualcosa del genere

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

8
Nel tuo esempio di dictinvece di una chiave, otterrai errori perché il valore di ritorno di .appendè Nonee la chiamata Nonedà un AttributeError. In generale, tuttavia, sono d'accordo con questo metodo.
SethMMorton,

2
il dict invece di una chiave è sbagliato, otterrai Mylist = ['c', 'd'] quando il dizionario viene inizializzato anche se hai commentato la parte "for..loop"
Mahmoud Elshahat

1
Nel tuo primo esempio filtersarebbe meglio di map, poiché restituirà solo i casi in cui lambda valuta true
Alex

1
Una comprensione è molto più semplice di una mappa di un lambda:any(v == 0 for v in (x, y, z))
wjandrea

35

Se sei molto pigro, puoi inserire i valori in un array. Ad esempio

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

Puoi anche inserire numeri e lettere in un dizionario e farlo, ma questo è probabilmente MOLTO più complicato di una semplice istruzione if. Ecco cosa ottieni cercando di essere più pigro :)

Un'altra cosa, la tua

if x or y or z == 0:

compilerà, ma non nel modo desiderato. Quando si inserisce semplicemente una variabile in un'istruzione if (esempio)

if b

il programma verificherà se la variabile non è nulla. Un altro modo di scrivere la frase sopra (che ha più senso) è

if bool(b)

Bool è una funzione integrata in Python che fondamentalmente esegue il comando di verifica di un'istruzione booleana (se non sai di cosa si tratta, è quello che stai cercando di fare nella tua istruzione if in questo momento :))

Un altro modo pigro che ho trovato è:

if any([x==0, y==0, z==0])

3
-1 C'è molta cattiva pratica qui. listè un built-in Python; usa invece un altro nome, come xyzad esempio. Perché costruisci la lista in quattro passaggi quando puoi farne uno, cioè xyz = [x, y, z]? Non usare elenchi paralleli, usa invece un dict. Tutto sommato, questa soluzione è molto più complicata di quella di ThatGuyRussell . Anche per l'ultima parte, perché non fare una comprensione, cioè any(v == 0 for v in (x, y, z))? Anche le matrici sono qualcos'altro in Python.
wjandrea,

32

Per verificare se un valore è contenuto in un insieme di variabili è possibile utilizzare i moduli integrati itertoolseoperator .

Per esempio:

importazioni:

from itertools import repeat
from operator import contains

Dichiarare variabili:

x = 0
y = 1
z = 3

Creare una mappatura dei valori (nell'ordine che si desidera verificare):

check_values = (0, 1, 3)

Utilizzare itertoolsper consentire la ripetizione delle variabili:

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

Infine, usa la mapfunzione per creare un iteratore:

checker = map(contains, check_vars, check_values)

Quindi, quando si verificano i valori (nell'ordine originale), utilizzare next():

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

eccetera...

Questo ha un vantaggio rispetto al lambda x: x in (variables)perché operatorè un modulo integrato ed è più veloce ed efficiente dell'usolambda che deve creare una funzione personalizzata sul posto.

Un'altra opzione per verificare se esiste un valore diverso da zero (o Falso) in un elenco:

not (x and y and z)

Equivalente:

not all((x, y, z))

2
Questo non risponde alla domanda del PO. Copre solo il primo caso nell'esempio fornito.
wallacer

31

Set è il buon approccio qui, perché ordina le variabili, quello che sembra essere il tuo obiettivo qui. {z,y,x}è {0,1,3}qualunque sia l'ordine dei parametri.

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

In questo modo, l'intera soluzione è O (n).


5
Dovresti aggiungere una descrizione di ciò che compie il tuo codice e di come lo fa. Le risposte brevi usando solo il codice sono sconsigliate
Raniz,

31

Tutte le eccellenti risposte fornite qui si concentrano sul requisito specifico del poster originale e si concentrano sulla if 1 in {x,y,z}soluzione proposta da Martijn Pieters.
Ciò che ignorano è la più ampia implicazione della domanda:
come testare una variabile rispetto a più valori?
La soluzione fornita non funzionerà per hit parziali se si utilizzano stringhe, ad esempio:
Verificare se la stringa "Wild" si trova in più valori

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

o

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

per questo scenario è più semplice convertire in una stringa

>>> [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

Va notato tuttavia, come detto @codeforester, che i limiti di parola vengono persi con questo metodo, come in:

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

le 3 lettere rotesistono in combinazione nell'elenco ma non come una singola parola. Il test per "marcire" fallirebbe ma se una delle voci dell'elenco fosse "marcisce all'inferno", anche questo fallirebbe.
In conclusione, fai attenzione ai tuoi criteri di ricerca se usi questo metodo e tieni presente che ha questa limitazione.


30

Penso che questo lo gestirà meglio:

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]

Produzione:

print validate(0, 8, 9)
c
print validate(9, 8, 9)
None
print validate(9, 8, 2)
e

30

Se si desidera utilizzare if, altrimenti le seguenti istruzioni rappresentano un'altra soluzione:

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)

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]

26

Questo codice può essere 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;

12

Puoi provare il metodo mostrato di seguito. In questo metodo, avrai la libertà di specificare / inserire il numero di variabili che desideri inserire.

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']

10

Una soluzione di linea:

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

O:

mylist = ['cdef'[i] for i in range(4) if i in (x, y, z)]

9

Forse hai bisogno di una formula diretta per i bit di output impostati.

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

Mappiamo a bit: 'c':1 'd':0xb10 'e':0xb100 'f':0xb1000

Relazione di isc (è 'c'):

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

Usa la matematica se la formula 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))

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

...

Connetti queste formule seguendo la logica:

  • logica and è la somma dei quadrati delle equazioni
  • la logica orè il prodotto di equazioni

e avrai una somma espressa totale di equazione e avrai una formula totale di somma

quindi la somma & 1 è c, la somma & 2 è d, la somma & 4 è e, la somma & 5 è f

Successivamente, è possibile formare una matrice predefinita in cui l'indice degli elementi stringa corrisponderebbe alla stringa pronta.

array[sum] ti dà la stringa.


7

Può essere fatto facilmente come

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

6

Il modo più mnemonico di rappresentare il tuo pseudo-codice in Python sarebbe:

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")

1
Questo approccio è più universale di `if 2 in (x, y, z): mylist.append ('e')` perché permette confronti arbitrari (es if any(v >= 42 for v in (x, y, z)):.). E le prestazioni di tutti e 3 i metodi ( 2 in {x,y,z}, 2 in (x,y,z), any(_v == 2 for _v in (x,y,z))) sembra essere quasi la stessa in CPython3.6 (vedi Gist )
imposeren

5

Per testare più variabili con un singolo valore: if 1 in {a,b,c}:

Per testare più valori con una variabile: if a in {1, 2, 3}:


4

Sembra che tu stia costruendo una sorta di cifra di Cesare.

Un approccio molto più generalizzato è questo:

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

uscite

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

Non sono sicuro che si tratti dell'effetto collaterale desiderato del tuo codice, ma l'ordine del tuo output verrà sempre ordinato.

Se questo è ciò che desideri, l'ultima riga può essere modificata in:

sorted([chr(val + origo) for val in inputs])

2

Puoi usare il dizionario:

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

1
Questo può aggiungere lo stesso più di una volta. Impostato?
Sergei,

2

Senza dettatura, prova questa soluzione:

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

e dà:

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

0

Questo ti aiuterà.

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);

0

Puoi unire questo

x = 0
y = 1
z = 3

in una variabile.

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

Cambia le nostre condizioni come:

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") 

Produzione:

In [21]: mylist                                                                                
Out[21]: ['c', 'd', 'f']

0

Problema

Mentre il modello per testare più valori

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

è molto leggibile e funziona in molte situazioni, c'è una trappola:

>>> 0 in {True, False}
True

Ma vogliamo avere

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

Soluzione

Una generalizzazione dell'espressione precedente si basa sulla risposta di ytpillai :

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

che può essere scritto come

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

Mentre questa espressione restituisce il risultato giusto, non è leggibile come la prima espressione :-(

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.