¿Cómo probar múltiples variables contra un valor?


645

Estoy tratando de hacer una función que compare múltiples variables con un número entero y genere una cadena de tres letras. Me preguntaba si había una manera de traducir esto a Python. Así que di:

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

que devolvería una lista de:

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

¿Es posible algo así?


55
usar 1en (tupla)

2
Cuando desee evaluar una lista de declaraciones de cualquier manera, puede usar any/ allfunciones. Por ejemplo: all([1, 2, 3, 4, False])devolverá False all([True, 1, 2, 3])devolverá True any([False, 0, 0, False])devolverá False any([False, 0, True, False])devolverá True
eddd

44
Esta pregunta es un objetivo duplicado muy popular, pero creo que es subóptima para ese propósito. La mayoría de las personas intentan hacer algo como if x == 0 or 1:, que por supuesto es similar if x or y == 0:, pero puede ser un poco confuso para los novatos. Dado el gran volumen de "¿Por qué no estoy x == 0 or 1trabajando?" preguntas, preferiría usar esta pregunta como nuestro objetivo duplicado canónico para estas preguntas.
Aran-Fey

1
Tenga especial cuidado al comparar valores "falsey" como 0, 0.0o False. Puede escribir fácilmente un código incorrecto que da la respuesta "correcta".
smci

Respuestas:


850

No comprende cómo funcionan las expresiones booleanas; no funcionan como una oración en inglés y supongo que estás hablando de la misma comparación para todos los nombres aquí. Estás buscando:

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

xy de ylo contrario se evalúan por su cuenta ( Falsesi 0, de lo Truecontrario).

Puede acortar eso usando una prueba de contención contra una tupla :

if 1 in (x, y, z):

o mejor aún:

if 1 in {x, y, z}:

usando aset para aprovechar la prueba de membresía de costo constante ( intoma una cantidad fija de tiempo, sea cual sea el operando de la izquierda).

Cuando lo usa or, python ve cada lado del operador como expresiones separadas . La expresión x or y == 1se trata primero como una prueba booleana x, luego, si eso es falso, y == 1se prueba la expresión .

Esto se debe a la precedencia del operador . El oroperador tiene una precedencia más baja que la ==prueba, por lo que este último se evalúa primero .

Sin embargo, incluso si este no fuera el caso, y la expresión en x or y or z == 1realidad se interpretara en su (x or y or z) == 1lugar, esto todavía no haría lo que espera que haga.

x or y or zevaluaría el primer argumento que es 'verdadero', por ejemplo, no False, numérico 0 o vacío (ver expresiones booleanas para detalles sobre lo que Python considera falso en un contexto booleano).

Entonces, para los valores x = 2; y = 1; z = 0, x or y or zse resolvería 2, porque ese es el primer valor verdadero en los argumentos. Entonces 2 == 1sería False, aunque y == 1sería True.

Lo mismo se aplicaría a la inversa; probar múltiples valores contra una sola variable; x == 1 or 2 or 3fallaría por las mismas razones. Use x == 1 or x == 2 or x == 3o x in {1, 2, 3}.


116
No sería tan rápido para la setversión. Las tuplas son muy baratas de crear e iterar. Al menos en mi máquina, las tuplas son más rápidas que las series siempre que el tamaño de la tupla sea de alrededor de 4-8 elementos. Si tiene que escanear más que eso, use un conjunto, pero si está buscando un elemento de entre 2 y 4 posibilidades, ¡una tupla es aún más rápida! Si se puede organizar para el caso más probable que sea por primera vez en la tupla, la victoria es aún más grande: (mi prueba: timeit.timeit('0 in {seq}'.format(seq=tuple(range(9, -1, -1)))))
SingleNegationElimination

57
@dequestarmappartialsetattr: en Python 3.3 y versiones posteriores, el conjunto se almacena como una constante, omitiendo el tiempo de creación por completo, eliminando el tiempo de creación. Las tuplas pueden ser baratas de crear, ya que Python almacena en caché un paquete de ellas para evitar la pérdida de memoria, lo que hace la mayor diferencia con los conjuntos aquí.
Martijn Pieters

13
@dequestarmappartialsetattr: Si cronometra solo la prueba de membresía, los conjuntos enteros y las tuplas son igualmente rápidos para el escenario ideal; haciendo coincidir el primer elemento. Después de eso, las tuplas pierden en sets.
Martijn Pieters

17
@MartijnPieters: Usar la setnotación literal para esta prueba no es un ahorro a menos que el contenido del setliteral también sea literal, ¿verdad? if 1 in {x, y, z}:no puede almacenar en caché set, porque x, yy zpodría cambiar, por lo que cualquiera de las soluciones necesita construir una tupleo setdesde cero, y sospecho que cualquier ahorro de búsqueda que pueda obtener al verificar la membresía se verá afectado por un mayor settiempo de creación.
ShadowRanger

99
@ShadowRanger: sí, la optimización de mirilla (ya sea para in [...]o in {...}) solo funciona si los contenidos de la lista o conjunto también son literales inmutables.
Martijn Pieters

96

Su problema se aborda más fácilmente con una estructura de diccionario como:

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 incluso lo d = "cdef"que lleva aMyList = ["cdef"[k] for k in [x, y, z]]
aragaer

99
omap(lambda i: 'cdef'[i], [x, y, z])
dansalmo

3
@MJM el orden de salida no está determinado por el dict, está determinado por el orden de la lista[x, y, z]
dansalmo

1
Aparte de la comprensión de la lista a la que aún no estoy completamente acostumbrado, la mayoría de nosotros tenía el mismo reflejo: ¡construir ese dict!
LoneWanderer

66

Como afirmó Martijn Pieters, el formato correcto y más rápido es:

if 1 in {x, y, z}:

Usando su consejo, ahora tendría declaraciones if separadas para que Python lea cada declaración si las anteriores eran Trueo False. Como:

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

Esto funcionará, pero si se siente cómodo usando diccionarios (vea lo que hice allí), puede limpiar esto haciendo un diccionario inicial que asigne los números a las letras que desee, luego simplemente use un bucle 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

La forma directa de escribir x or y or z == 0es

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

Pero no creo que te guste. :) Y de esta manera es feo.

La otra forma (mejor) es:

0 in (x, y, z)

Por cierto, muchos ifs podrían escribirse como algo así

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
En su ejemplo de en dictlugar de una clave, obtendrá errores porque el valor de retorno de .appendes None, y al llamar Noneda un AttributeError. Sin embargo, en general estoy de acuerdo con este método.
SethMMorton

2
el dict en lugar de una clave es incorrecto, obtendrá Mylist = ['c', 'd'] cuando el diccionario se inicialice incluso si comentó la parte "for..loop"
Mahmoud Elshahat

1
En su primer ejemplo filter, sería mejor que map, ya que solo devolverá instancias en las que lambda se evalúe como verdadero
Alex

1
Una comprensión es mucho más simple que un mapa de un lambda:any(v == 0 for v in (x, y, z))
wjandrea

35

Si eres muy muy vago, puedes poner los valores dentro de una matriz. Como

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

También puede poner los números y letras en un diccionario y hacerlo, pero esto es probablemente MUCHO más complicado que simplemente las declaraciones if. Eso es lo que obtienes por tratar de ser más vago :)

Una cosa más, tu

if x or y or z == 0:

se compilará, pero no de la manera que desea. Cuando simplemente pones una variable en una declaración if (ejemplo)

if b

el programa verificará si la variable no es nula. Otra forma de escribir la declaración anterior (que tiene más sentido) es

if bool(b)

Bool es una función incorporada en python que básicamente hace el comando de verificar una declaración booleana (si no sabe qué es eso, es lo que está tratando de hacer en su declaración if en este momento :))

Otra forma perezosa que encontré es:

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

3
-1 Hay muchas malas prácticas aquí. listes un Python incorporado; use otro nombre en su lugar, como xyzpor ejemplo. ¿Por qué construyes la lista en cuatro pasos cuando puedes hacer uno, es decir xyz = [x, y, z]? No use listas paralelas, use un dict en su lugar. En general, esta solución es mucho más complicada que la de ThatGuyRussell . También para la última parte, ¿por qué no hacer una comprensión, es decir any(v == 0 for v in (x, y, z))? También las matrices son algo más en Python.
wjandrea

32

Para verificar si un valor está contenido dentro de un conjunto de variables, puede usar los módulos incorporados itertoolsy operator.

Por ejemplo:

Importaciones:

from itertools import repeat
from operator import contains

Declarar variables:

x = 0
y = 1
z = 3

Cree una asignación de valores (en el orden que desea verificar):

check_values = (0, 1, 3)

Use itertoolspara permitir la repetición de las variables:

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

Finalmente, use la mapfunción para crear un iterador:

checker = map(contains, check_vars, check_values)

Luego, al verificar los valores (en el orden original), use next():

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

etc ...

Esto tiene una ventaja sobre el lambda x: x in (variables)porque operatores un módulo incorporado y es más rápido y más eficiente que usar el lambdaque tiene que crear una función in situ personalizada.

Otra opción para verificar si hay un valor distinto de cero (o Falso) en una lista:

not (x and y and z)

Equivalente:

not all((x, y, z))

2
Esto no responde la pregunta del OP. Solo cubre el primer caso en el ejemplo proporcionado.
wallacer

31

Establecer es el buen enfoque aquí, porque ordena las variables, lo que parece ser su objetivo aquí. {z,y,x}es {0,1,3}cualquiera que sea ​​el orden de los parámetros.

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

De esta manera, toda la solución es O (n).


55
Debe agregar una descripción de lo que logra su código y cómo lo hace. Se desaconsejan las respuestas cortas que usan solo código
Raniz

31

Todas las excelentes respuestas proporcionadas aquí se concentran en los requisitos específicos del póster original y se concentran en la if 1 in {x,y,z}solución presentada por Martijn Pieters.
Lo que ignoran es la implicación más amplia de la pregunta:
¿cómo pruebo una variable contra varios valores?
La solución provista no funcionará para los accesos parciales si se usan cadenas, por ejemplo:
Pruebe si la cadena "Wild" tiene varios valores

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

para este escenario, es más fácil convertir a una cadena

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

Sin embargo, debe tenerse en cuenta, como se mencionó anteriormente @codeforester, que los límites de palabras se pierden con este método, como en:

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

las 3 letras rotexisten en combinación en la lista pero no como una palabra individual. Las pruebas de "podredumbre" fallarían, pero si uno de los elementos de la lista fuera "podredumbre", también fallaría.
En resumen, tenga cuidado con sus criterios de búsqueda si usa este método y tenga en cuenta que tiene esta limitación.


30

Creo que esto lo manejará mejor:

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]

Salida:

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

30

Si desea usar if, las siguientes declaraciones son otra solución:

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

Este código puede ser útil.

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

Puede probar el método que se muestra a continuación. En este método, tendrá la libertad de especificar / ingresar el número de variables que desea ingresar.

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

Solución de una línea:

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

Tal vez necesite una fórmula directa para el conjunto de bits de salida.

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

Vamos a mapear en bits: 'c':1 'd':0xb10 'e':0xb100 'f':0xb1000

Relación de isc (es 'c'):

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

Use las matemáticas si la fórmula 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))

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

...

Conecte estas fórmulas siguiendo la lógica:

  • la lógica andes la suma de cuadrados de ecuaciones
  • la lógica ores el producto de ecuaciones

y tendrás una ecuación total de suma expresa y tienes una fórmula total de suma

entonces sum & 1 es c, sum & 2 es d, sum & 4 es e, sum & 5 es f

Después de esto, puede formar una matriz predefinida donde el índice de elementos de cadena correspondería a la cadena lista.

array[sum] te da la cuerda.


7

Se puede hacer fácilmente como

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

6

La forma más nemotécnica de representar su pseudocódigo en Python sería:

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
Este enfoque es más universal que `if 2 en (x, y, z): mylist.append ('e')` porque permite comparaciones arbitrarias (p if any(v >= 42 for v in (x, y, z)):. Ej .). Y el rendimiento de los 3 métodos ( 2 in {x,y,z}, 2 in (x,y,z), any(_v == 2 for _v in (x,y,z))) parece ser casi lo mismo en CPython3.6 (ver Gist )
imposeren

5

Para probar múltiples variables con un solo valor: if 1 in {a,b,c}:

Para probar múltiples valores con una variable: if a in {1, 2, 3}:


4

Parece que estás construyendo algún tipo de cifrado César.

Un enfoque mucho más generalizado es este:

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

salidas

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

No estoy seguro si es un efecto secundario deseado de su código, pero el orden de su salida siempre se ordenará.

Si esto es lo que desea, la línea final se puede cambiar a:

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

2

Puedes usar el diccionario:

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
Esto puede agregar lo mismo más de una vez. ¿Conjunto?
Sergei

2

Sin dict, prueba esta solución:

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

y da:

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

0

Esto te ayudara.

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

Puedes unir esto

x = 0
y = 1
z = 3

en una variable

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

Cambia nuestras condiciones como:

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

Salida:

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

0

Problema

Mientras que el patrón para probar múltiples valores

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

es muy legible y funciona en muchas situaciones, hay una trampa:

>>> 0 in {True, False}
True

Pero queremos tener

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

Solución

Una generalización de la expresión anterior se basa en la respuesta de ytpillai :

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

que se puede escribir como

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

Si bien esta expresión devuelve el resultado correcto, no es tan legible como la primera expresión :-(

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.