Как проверить несколько переменных против значения?


645

Я пытаюсь сделать функцию, которая будет сравнивать несколько переменных с целым числом и выводить строку из трех букв. Мне было интересно, если есть способ перевести это на Python. Так сказать:

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

который бы возвратил список:

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

Возможно ли что-то подобное?


5
использовать 1в (кортеж)

2
Если вы хотите оценить список утверждений любым способом, вы можете использовать any/ allfunctions. Например: all([1, 2, 3, 4, False])вернет False all([True, 1, 2, 3])вернет True any([False, 0, 0, False])вернет False any([False, 0, True, False])вернет True
eddd

4
Этот вопрос является очень популярной двойной целью, но я думаю, что он неоптимален для этой цели. Большинство людей пытаются сделать что-то подобное if x == 0 or 1:, что, конечно, похоже if x or y == 0:, но, тем не менее, может быть немного запутанным для новичков. Учитывая огромное количество "Почему я не x == 0 or 1работаю?" вопросы, я бы предпочел использовать этот вопрос в качестве нашей цели канонического дублирования для этих вопросов.
Аран-Фей

1
Соблюдайте особую осторожность при сравнении с такими значениями «фальси», как 0, 0.0или False. Вы можете легко написать неправильный код, который дает «правильный» ответ.
19

Ответы:


851

Вы неправильно понимаете, как работают логические выражения; они не работают как английское предложение и предполагают, что вы говорите об одном и том же сравнении для всех имен здесь. Ты ищешь:

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

xи yв противном случае оцениваются самостоятельно (False если 0, в Trueпротивном случае).

Вы можете сократить это, используя тест сдерживания для кортежа :

if 1 in (x, y, z):

или еще лучше:

if 1 in {x, y, z}:

с помощью помощьюset теста членства в постоянных ценах (in занимает фиксированное количество времени, независимо от того, какой левый операнд).

Когда вы используете or, python видит каждую сторону оператора как отдельные выражения. Выражение x or y == 1рассматривается как первый логический тест дляx , затем, если это False, выражениеy == 1 проверяется.

Это связано с приоритетом оператора . orОператор имеет более низкий приоритет , чем== тест, так что последний оцениваются первым .

Однако, даже если бы этого не было так, и выражение на x or y or z == 1самом деле интерпретировалось как(x or y or z) == 1 вместо этого, это все равно не сделало бы того, что вы ожидаете.

x or y or z оценил бы первый аргумент, который является «правдивым», например, не False , числовой 0 или пустой (см. логические выражения для деталей того, что Python считает ложным в логическом контексте).

Таким образом , для значений x = 2; y = 1; z = 0, x or y or zразрешило бы к 2, потому что это первое истинно , как значение аргументов. Тогда 2 == 1бы False, хотяy == 1 бы True.

То же самое относится к обратному; тестирование нескольких значений по одной переменной; x == 1 or 2 or 3потерпит неудачу по тем же причинам. Используйте x == 1 or x == 2 or x == 3или x in {1, 2, 3}.


116
Я бы не стал так быстро переходить на setверсию. Кортежи очень дешевы в создании и повторении. По крайней мере, на моей машине кортежи работают быстрее, чем наборы, при условии, что размер кортежа составляет около 4-8 элементов. Если вам нужно сканировать больше, используйте набор, но если вы ищете предмет из 2-4 вариантов, кортеж будет еще быстрее! Если вы можете организовать для наиболее вероятного случая , чтобы быть первым в кортеже, выигрыш еще больше: (мой тест: timeit.timeit('0 in {seq}'.format(seq=tuple(range(9, -1, -1)))))
SingleNegationElimination

57
@dequestarmappartialsetattr: В Python 3.3 и выше, набор сохраняется как константа, полностью минуя время создания, исключая время создания. Кортежи могут быть дешевыми в создании, так как Python кэширует их несколько, чтобы избежать оттока памяти, что делает это самым большим отличием от наборов здесь.
Мартин Питерс

13
@dequestarmappartialsetattr: Если вы тестируете время только на членство, целые множества и кортежи одинаково быстры для идеального сценария; соответствие первого элемента. После этого кортежи проигрывают сетам.
Мартин Питерс

17
@MartijnPieters: использование setлитеральной нотации для этого теста не является экономией, если содержимое setлитерала также не является литералом, верно? if 1 in {x, y, z}:не может кэшировать set, так как x, yи zможет измениться, поэтому либо потребности решение построить tupleили setс нуля, и я подозреваю , что все , что LookUp сбережения вы можете получить при проверке на членство будет завален большим setвременем создания.
ShadowRanger

9
@ShadowRanger: да, оптимизация глазка (будь то для in [...]или in {...}) работает только в том случае, если содержимое списка или набора также является неизменяемым литералом.
Мартин Питерс

96

Ваша проблема легче решается с помощью словарной структуры, такой как:

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
Или даже d = "cdef"что приводит кMyList = ["cdef"[k] for k in [x, y, z]]
aragaer

9
илиmap(lambda i: 'cdef'[i], [x, y, z])
дансальмо

3
@MJM порядок вывода не определяется dict, он определяется порядком списка[x, y, z]
dansalmo

1
Помимо понимания списка, к которому я еще не полностью привык, у большинства из нас был тот же самый рефлекс: строить это диктовать!
Одинокий Странник

66

Как утверждает Martijn Pieters, правильный и самый быстрый формат:

if 1 in {x, y, z}:

Используя его совет, теперь вы будете иметь отдельные операторы if, так что Python будет читать каждое утверждение независимо от того, были ли они первыми Trueили False. Такие как:

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

Это будет работать, но если вам удобно пользоваться словарями (посмотрите, что я там делал), вы можете очистить это, сделав начальный словарь, отображающий числа в нужные вам буквы, а затем просто используя цикл 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

Прямой способ записи x or y or z == 0является

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

Но я не думаю, тебе это нравится. :) И этот путь безобразен.

Другой способ (лучше):

0 in (x, y, z)

Кстати, многие ifs могут быть написаны как-то так

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
В вашем примере dictвместо ключа вы получите ошибки, потому что возвращаемое значение .append- это None, а вызов Noneдает AttributeError. В целом я согласен с этим методом, хотя.
SethMMorton

2
неправильный дикт вместо ключа, вы получите Mylist = ['c', 'd'], когда словарь будет инициализирован, даже если вы закомментировали часть "for..loop"
Махмуд Эльшахат

1
В вашем первом примере filterбудет лучше, чем map, поскольку он вернет только те случаи, когда лямбда оценивается как истина
Алекс

1
any(v == 0 for v in (x, y, z))
Понимание

35

Если вы очень ленивы, вы можете поместить значения в массив. Такие как

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

Вы также можете поместить цифры и буквы в словарь и сделать это, но это, вероятно, намного сложнее, чем просто операторы if. Это то, что вы получаете за попытку быть очень ленивым :)

Еще одна вещь, ваш

if x or y or z == 0:

будет компилироваться, но не так, как вы этого хотите. Когда вы просто помещаете переменную в оператор if (пример)

if b

программа проверит, не является ли переменная нулевой. Другой способ написать вышеупомянутое утверждение (которое имеет больше смысла)

if bool(b)

Bool - это встроенная функция в python, которая в основном выполняет команду проверки логического выражения (если вы не знаете, что это такое, это то, что вы пытаетесь сделать в своем выражении if прямо сейчас :))

Еще один ленивый способ, который я нашел:

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

3
-1 Здесь много плохой практики. listявляется встроенным Python; вместо этого используйте другое имя, как xyzнапример. Почему вы построить список в четыре этапа , когда вы можете сделать один, то есть xyz = [x, y, z]? Не используйте параллельные списки, вместо этого используйте dict. В общем, это решение гораздо более замысловатое, чем у ThatGuyRussell . Также в последней части, почему бы не сделать понимание, то есть any(v == 0 for v in (x, y, z))? Также массивы - это что-то еще в Python.
wjandrea

32

Чтобы проверить, содержится ли значение в наборе переменных, вы можете использовать встроенные модули itertoolsиoperator .

Например:

Импорт:

from itertools import repeat
from operator import contains

Объявите переменные:

x = 0
y = 1
z = 3

Создайте отображение значений (в порядке, который вы хотите проверить):

check_values = (0, 1, 3)

Используйте, itertoolsчтобы разрешить повторение переменных:

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

Наконец, используйте mapфункцию для создания итератора:

checker = map(contains, check_vars, check_values)

Затем при проверке значений (в исходном порядке) используйте next():

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

так далее...

Это имеет преимущество перед тем, lambda x: x in (variables)что operatorявляется встроенным модулем и является более быстрым и более эффективным, чем использование, lambdaкоторое должно создавать пользовательскую функцию на месте.

Еще один вариант проверки наличия ненулевого (или ложного) значения в списке:

not (x and y and z)

Эквивалент:

not all((x, y, z))

2
Это не отвечает на вопрос ОП. Он охватывает только первый случай в приведенном примере.
Уоллесер

31

Set - это хороший подход, потому что он упорядочивает переменные, что, по-видимому, и является вашей целью. {z,y,x}является {0,1,3}то , что порядок параметров.

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

Таким образом, все решение O (n).


5
Вы должны добавить описание того, что ваш код выполняет и как он это делает. Короткие ответы с использованием только кода не рекомендуется
Ранис

31

Все прекрасные ответы, представленные здесь, концентрируются на конкретных требованиях оригинального плаката и концентрируются на if 1 in {x,y,z}решении, предложенном Мартином Питерсом.
Что они игнорируют, так это более широкий смысл вопроса:
как проверить одну переменную на множественные значения?
Предоставленное решение не будет работать для частичных попаданий, если используются строки, например:
Проверить, если строка «Wild» имеет несколько значений

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

или

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

для этого сценария проще всего преобразовать в строку

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

Однако следует отметить, что, как уже упоминалось @codeforester, при использовании этого метода границы слов теряются, например:

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

3 буквы rotсуществуют в комбинации в списке, но не как отдельное слово. Проверка на «гниль» не удалась бы, но если бы один из элементов списка был «гнилой в аду», это тоже не сработало бы.
В результате будьте внимательны с критериями поиска, если используете этот метод, и имейте в виду, что у него есть это ограничение.


30

Я думаю, что это справится лучше:

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]

Вывод:

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

30

Если вы хотите использовать оператор if, else - это другое решение:

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

Этот код может быть полезен

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

Вы можете попробовать метод, показанный ниже. В этом методе у вас будет свобода указывать / вводить количество переменных, которые вы хотите ввести.

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


9

Возможно, вам нужна прямая формула для набора выходных битов.

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

Давайте сопоставим на биты: 'c':1 'd':0xb10 'e':0xb100 'f':0xb1000

Отношение isc (is 'c'):

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

Используйте математическую формулу, если https://youtu.be/KAdKCgBGK0k?list=PLnI9xbPdZUAmUL8htSl6vToPQRRN3hhFp&t=315

[С]: (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))

...

Соедините эти формулы, используя следующую логику:

  • логика andесть сумма квадратов уравнений
  • логика orесть произведение уравнений

и вы будете иметь общую сумму экспресс-сумму, и у вас есть общая формула суммы

тогда сумма & 1 - это c, сумма & 2 - это d, сумма & 4 - это e, сумма & 5 - это f

После этого вы можете сформировать предопределенный массив, где индекс строковых элементов будет соответствовать готовой строке.

array[sum] дает вам строку.


7

Это можно сделать легко, как

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

6

Наиболее мнемонический способ представления вашего псевдокода в Python:

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
Этот подход более универсален, чем `if 2 in (x, y, z): mylist.append ('e')`, поскольку допускает произвольные сравнения (например if any(v >= 42 for v in (x, y, z)):). И выполнение всех 3 -х методов ( 2 in {x,y,z}, 2 in (x,y,z), any(_v == 2 for _v in (x,y,z))) , кажется, почти то же самое в CPython3.6 (см GIST )
imposeren

5

Чтобы проверить несколько переменных с одним значением: if 1 in {a,b,c}:

Чтобы проверить несколько значений с одной переменной: if a in {1, 2, 3}:


4

Похоже, вы создаете какой-то шифр Цезаря.

Гораздо более обобщенный подход заключается в следующем:

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

выходы

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

Не уверен, является ли это желаемым побочным эффектом вашего кода, но порядок вывода всегда будет отсортирован.

Если это то, что вы хотите, финальную строку можно изменить на:

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

2

Вы можете использовать словарь:

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
Это может добавить то же самое, чем когда-то это. Набор?
Сергей

2

Без слов попробуйте это решение:

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

и дает:

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

0

Это поможет вам.

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

Вы можете объединить это

x = 0
y = 1
z = 3

в одной переменной.

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

Изменить наши условия как:

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

Вывод:

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

0

проблема

Хотя шаблон для тестирования нескольких значений

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

очень читабелен и работает во многих ситуациях, есть одна ловушка:

>>> 0 in {True, False}
True

Но мы хотим иметь

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

Решение

Одно обобщение предыдущего выражения основано на ответе ytpillai :

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

который можно записать как

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

Хотя это выражение возвращает правильный результат, оно не так доступно для чтения, как первое выражение :-(

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.