Como testar várias variáveis ​​em relação a um valor?


645

Estou tentando criar uma função que compare várias variáveis ​​com um número inteiro e produza uma sequência de três letras. Fiquei me perguntando se havia uma maneira de traduzir isso em Python. Então diga:

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 retornaria uma lista de:

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

É algo assim possível?


5
use 1em (tupla)

2
Quando você deseja avaliar uma lista de instruções de qualquer maneira, pode usar any/ allfunções. Por exemplo: all([1, 2, 3, 4, False])retornará Falso all([True, 1, 2, 3])retornará Verdadeiro any([False, 0, 0, False])retornará Falso any([False, 0, True, False])retornará Verdadeiro
— eddd 4/18/18

4
Essa pergunta é um destino duplicado muito popular, mas acho que é abaixo do ideal para esse propósito. A maioria das pessoas tenta fazer algo parecido if x == 0 or 1:, o que obviamente é semelhante if x or y == 0:, mas pode ser um pouco confuso para iniciantes. Dado o grande volume de "Por que meu x == 0 or 1trabalho não está ?" Eu prefiro usar essa pergunta como nosso alvo duplicado canônico para essas perguntas.
— Aran-Fey

1
Tome muito cuidado ao comparar com valores "falsey" como 0, 0.0ou False. Você pode escrever facilmente um código errado, que fornece a resposta "certa".
— SMCI

Respostas:


850

Você entende mal como expressões booleanas funcionam; eles não funcionam como uma sentença em inglês e acho que você está falando da mesma comparação para todos os nomes aqui. Você está procurando:

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

xe ysão avaliados de outra forma por conta própria ( Falsese 0, de Trueoutra forma).

Você pode reduzi-lo usando um teste de contenção em relação a uma tupla :

if 1 in (x, y, z):

ou melhor ainda:

if 1 in {x, y, z}:

usar aset para tirar proveito do teste de associação de custo constante ( inleva um tempo fixo, independentemente do operando do lado esquerdo).

Quando você usa or, o python vê cada lado do operador como expressões separadas . A expressão x or y == 1é tratada como primeiro um teste booleano e x, se for False, a expressão y == 1é testada.

Isso ocorre devido à precedência do operador . O oroperador tem uma precedência mais baixa que o ==teste, portanto, este último é avaliado primeiro .

No entanto, mesmo que não fosse esse o caso, e a expressão x or y or z == 1fosse realmente interpretada como (x or y or z) == 1alternativa, isso ainda não faria o que você espera.

x or y or zavaliaria o primeiro argumento que é 'verdade', por exemplo, não Falsenumérico 0 ou vazio (consulte expressões booleanas para obter detalhes sobre o que o Python considera falso em um contexto booleano).

Portanto, para os valores x = 2; y = 1; z = 0, x or y or zseria resolvido 2, porque esse é o primeiro valor verdadeiro nos argumentos. Então 2 == 1seria False, mesmo que y == 1fosse True.

O mesmo se aplicaria ao inverso; testando vários valores contra uma única variável; x == 1 or 2 or 3falharia pelas mesmas razões. Use x == 1 or x == 2 or x == 3ou x in {1, 2, 3}.


116
Eu não seria tão rápido em ir para a setversão. As tuplas são muito baratas para criar e iterar. Na minha máquina, pelo menos, as tuplas são mais rápidas que os conjuntos, desde que o tamanho da tupla esteja em torno de 4-8 elementos. Se você precisar digitalizar mais do que isso, use um conjunto, mas se estiver procurando por um item de 2 a 4 possibilidades, uma tupla é ainda mais rápida! Se você pode mandar para o caso mais provável de ser o primeiro na tupla, a vitória é ainda maior: (meu teste: timeit.timeit('0 in {seq}'.format(seq=tuple(range(9, -1, -1)))))
— SingleNegationElimination

57
@dequestarmappartialsetattr: No Python 3.3 e superior, o conjunto é armazenado como uma constante, ignorando completamente o tempo de criação, eliminando o tempo de criação. Tuples pode ser barato para criar como Python armazena em cache um pacote de-los a variação de memória evitar, fazendo com que a maior diferença com conjuntos aqui.
— Martijn Pieters

13
@dequestarmappartialsetattr: se você aplicar apenas o teste de associação, os conjuntos de números inteiros e as tuplas serão igualmente rápidos para o cenário ideal; combinando o primeiro elemento. Depois que as tuplas perdem para sets.
— Martijn Pieters

17
@MartijnPieters: Usar a setnotação literal para este teste não é uma economia, a menos que o conteúdo do setliteral também seja literal, certo? if 1 in {x, y, z}:não é possível armazenar em cache o setporque x, ye zpode mudar, portanto, qualquer solução precisa criar um tupleou a setpartir do zero, e eu suspeito que qualquer economia de pesquisa que você possa obter ao verificar a associação seria inundada por um maior settempo de criação.
— ShadowRanger

9
@ShadowRanger: sim, otimização peephole (seja para in [...]ou in {...}) só funciona se o conteúdo da lista ou conjunto são literais imutáveis também.
— Martijn Pieters

96

Seu problema é mais facilmente resolvido com uma estrutura de dicionário 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
Ou mesmo o d = "cdef"que leva aMyList = ["cdef"[k] for k in [x, y, z]]
— aragaer

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

3
@MJM a ordem de saída não é determinada pelo Dict, que é determinada pela ordem da lista[x, y, z]
— dansalmo

1
Além da compreensão da lista com a qual ainda não estou totalmente acostumado, a maioria de nós teve o mesmo reflexo: construa esse ditado!
— LoneWanderer

66

Conforme declarado por Martijn Pieters, o formato correto e mais rápido é:

if 1 in {x, y, z}:

Usando o conselho dele, você agora teria instruções if separadas, para que o Python leia cada instrução, seja ela anterior Trueou não False. Tal 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")
...

Isso funcionará, mas se você se sentir confortável usando dicionários (veja o que eu fiz lá), poderá limpar isso, criando um dicionário inicial mapeando os números para as letras desejadas e usando apenas um loop 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

A maneira direta de escrever x or y or z == 0é

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

Mas acho que você não gosta. :) E assim é feio.

A outra maneira (melhor) é:

0 in (x, y, z)

BTW muitos ifs poderia ser escrito como algo parecido com isto

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
No seu exemplo da dictchave em vez de uma chave, você receberá erros porque o valor de retorno de .appendé Nonee a chamada Nonefornece um AttributeError. Em geral, eu concordo com este método, no entanto.
— SethMMorton

2
o ditado em vez de uma chave estiver errado, você receberá Mylist = ['c', 'd'] quando o dicionário for inicializado, mesmo que você tenha comentado a parte "for..loop"
— Mahmoud Elshahat

1
Em seu primeiro exemplo filterseria melhor do que map, uma vez que irá retornar apenas instâncias onde avalia lambda para true
— Alex

1
Uma compreensão é muito mais simples que um mapa de um lambda:any(v == 0 for v in (x, y, z))
— wjandrea

35

Se você é muito preguiçoso, pode colocar os valores dentro de uma matriz. Tal 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

Você também pode colocar os números e letras em um dicionário e fazê-lo, mas isso provavelmente é MUITO mais complicado do que simplesmente declarações if. É isso que você ganha por tentar ser preguiçoso demais :)

Mais uma coisa, sua

if x or y or z == 0:

será compilado, mas não da maneira que você deseja. Quando você simplesmente coloca uma variável em uma instrução if (exemplo)

if b

o programa irá verificar se a variável não é nula. Outra maneira de escrever a afirmação acima (que faz mais sentido) é

if bool(b)

Bool é uma função embutida em python que basicamente executa o comando de verificar uma instrução booleana (se você não sabe o que é isso, é o que está tentando fazer na sua instrução if agora :))

Outra maneira preguiçosa que encontrei é:

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

3
-1 Há muita prática ruim aqui. listé um Python embutido; use outro nome, como xyzpor exemplo. Por que você constrói a lista em quatro etapas quando você pode executar uma, ou seja xyz = [x, y, z]? Não use listas paralelas, use um ditado. Em suma , esta solução é muito mais complicada do que a ThatGuyRussell . Também para a última parte, por que não fazer uma compreensão, ou seja any(v == 0 for v in (x, y, z))? Também matrizes são outra coisa em Python.
— wjandrea

32

Para verificar se um valor está contido em um conjunto de variáveis, você pode usar os módulos embutidos itertoolse operator.

Por exemplo:

Importações:

from itertools import repeat
from operator import contains

Declarar variáveis:

x = 0
y = 1
z = 3

Crie o mapeamento de valores (na ordem que você deseja verificar):

check_values = (0, 1, 3)

Use itertoolspara permitir a repetição das variáveis:

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

Por fim, use a mapfunção para criar um iterador:

checker = map(contains, check_vars, check_values)

Em seguida, ao verificar os valores (na ordem original), use next():

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

etc ...

Isso tem uma vantagem sobre o lambda x: x in (variables)porque operatoré um módulo embutido e é mais rápido e eficiente do que o uso do lambdaqual é necessário criar uma função personalizada no local.

Outra opção para verificar se existe um valor diferente de zero (ou Falso) em uma lista:

not (x and y and z)

Equivalente:

not all((x, y, z))

2
Isso não responde à pergunta do OP. Ele cobre apenas o primeiro caso no exemplo fornecido.
— wallacer

31

Set é a boa abordagem aqui, porque ordena as variáveis, qual parece ser seu objetivo aqui. {z,y,x}é {0,1,3}qualquer que seja a ordem dos parâmetros.

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

Dessa forma, toda a solução é O (n).


5
Você deve adicionar uma descrição do que seu código realiza e como ele o faz. Respostas curtas usando apenas o código é desencorajado
— Raniz

31

Todas as excelentes respostas fornecidas aqui concentram-se nos requisitos específicos do pôster original e na if 1 in {x,y,z}solução apresentada por Martijn Pieters.
O que eles ignoram é a implicação mais ampla da pergunta:
como testar uma variável em relação a vários valores?
A solução fornecida não funcionará para ocorrências parciais se estiver usando seqüências de caracteres, por exemplo:
Teste se a sequência "Wild" está em vários valores

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

para esse cenário, é mais fácil converter em uma string

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

Deve-se notar, no entanto, como mencionado por @codeforester, que as fronteiras das palavras são perdidas com esse método, como em:

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

as três letras rotexistem combinadas na lista, mas não como uma palavra individual. Testar a "podridão" falharia, mas se um dos itens da lista fosse "apodrecer no inferno", isso também falharia.
Como resultado, tenha cuidado com seus critérios de pesquisa se estiver usando esse método e esteja ciente de que ele tem essa limitação.


30

Eu acho que isso vai lidar melhor com isso:

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]

Resultado:

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

30

Se você deseja usar as instruções if, else, a seguir é outra solução:

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

Esse código pode 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

Você pode tentar o método mostrado abaixo. Neste método, você terá a liberdade de especificar / inserir o número de variáveis ​​que deseja inserir.

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

Solução de uma linha:

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

9

Talvez você precise de uma fórmula direta para o conjunto de bits de saída.

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 mapear para bits: 'c':1 'd':0xb10 'e':0xb100 'f':0xb1000

Relação de isc (é 'c'):

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

Use matemática se a 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))

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

...

Conecte essas fórmulas seguindo a lógica:

  • lógica andé a soma dos quadrados das equações
  • lógica oré o produto de equações

e você terá uma equação total de soma expressa e você tem a fórmula total de soma

então soma & 1 é c, soma & 2 é d, soma & 4 é e, soma & 5 é f

Depois disso, você pode formar uma matriz predefinida, onde o índice dos elementos da string corresponderia à string pronta.

array[sum] dá-lhe a corda.


7

Isso pode ser feito facilmente como

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

6

A maneira mais mnemônica de representar seu pseudo-código no Python seria:

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
Essa abordagem é mais universal do que `se 2 in (x, y, z): mylist.append ('e')` porque permite comparações arbitrárias (por exemplo if any(v >= 42 for v in (x, y, z)):). E o desempenho de todos os 3 métodos ( 2 in {x,y,z}, 2 in (x,y,z), any(_v == 2 for _v in (x,y,z))) parece ser quase o mesmo em CPython3.6 (veja Gist )
— imposeren

5

Para testar várias variáveis ​​com um único valor: if 1 in {a,b,c}:

Para testar vários valores com uma variável: if a in {1, 2, 3}:


4

Parece que você está construindo algum tipo de cifra de César.

Uma abordagem muito mais generalizada é a seguinte:

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

saídas

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

Não tenho certeza se é um efeito colateral desejado do seu código, mas a ordem da sua saída será sempre classificada.

Se é isso que você deseja, a linha final pode ser alterada para:

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

2

Você pode usar o dicionário:

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
Isso pode acrescentar o mesmo mais do que uma vez. Conjunto?
— Sergei

2

Sem dict, tente esta solução:

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

e dá:

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

0

Isso irá ajudá-lo.

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

Você pode unir isso

x = 0
y = 1
z = 3

em uma variável.

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

Altere nossas condições 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") 

Resultado:

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

0

Problema

Enquanto o padrão para testar vários valores

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

é muito legível e está funcionando em muitas situações, há uma armadilha:

>>> 0 in {True, False}
True

Mas nós queremos ter

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

Solução

Uma generalização da expressão anterior é baseada na resposta de ytpillai :

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

que pode ser escrito como

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

Embora essa expressão retorne o resultado correto, ela não é tão legível quanto a primeira expressão :-(

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.