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?

user1877442
fonte
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
1
Pelo contrário, consulte Comparando uma string com vários itens no Python
tripleee

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

Martijn Pieters
fonte
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]]
dansalmo
fonte
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])
ThatGuyRussell
fonte
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
akaRem
fonte
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])
ytpillai
fonte
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))
GuiltyDolphin
fonte
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).

BM
fonte
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.

Rolf da Saxônia
fonte
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
Bhargav Boda
fonte
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)
hamid
fonte
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
fonte
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;
michael zxc858
fonte
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']
Siddharth Satpathy
fonte
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)]
Vinayak Kaniyarakkal
fonte
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.

Sergei
fonte
7

Isso pode ser feito facilmente como

for value in [var1,var2,var3]:
     li.append("targetValue")
Seenivasan
fonte
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")
rsalmei
fonte
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}:

alamin
fonte
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])
firelynx
fonte
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
Rohit Gawas
fonte
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']
Massifox
fonte
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);
Mayur
fonte
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']
Serhii
fonte
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 :-(

fhgd
fonte