2013-02-27 2 views
334

Я пытаюсь сделать функцию, которая будет сравнивать несколько переменных с целым числом и вывести строку из трех букв. Мне было интересно, есть ли способ перевести это на 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"] 

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

+16

не должен быть вопрос "тест 'несколько' переменных против нескольких значений"? –

+0

use '1' in (tuple) – DNinja21

ответ

458

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

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.

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

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

x or y or z будет оценивать первый аргумент, который является «правдивым», например. а не False, числовое 0 или пустое (см. boolean expressions для получения более подробной информации о том, что 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}.

+0

вы можете объяснить немного больше, почему OP неправильно понимает логические значения? – Private

+55

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

+31

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

13

Прямой способ написать 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) 

BTW много if с может быть написана как-то вроде этого

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 
+2

В вашем примере 'dict' вместо ключа вы получите ошибки, потому что возвращаемое значение' .append' равно 'None', а вызов' None' дает 'AttributeError'. В общем, я согласен с этим методом. – SethMMorton

42

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

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

Или даже 'd =" cdef "', что приводит к 'MyList = [" cdef "[k] для k в [x, y, z]]' – aragaer

+7

или 'map (lambda i: 'cdef' [i], [x, y, z]) ' – dansalmo

9

Чтобы проверить, содержится ли значение в наборе переменных bles вы можете использовать встроенные модули 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, который должен создать пользовательскую функцию на месте.

Другой вариант для проверки, если есть ненулевая (или False) значение в списке:

not (x and y and z) 

Эквивалент:

not all((x, y, z)) 
+0

Это не отвечает на вопрос OP. Он охватывает только первый случай в приведенном примере. – wallacer

8

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

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

Если вы хотите использовать, если другое заявление следующего другое решение:

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

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

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 x or y or z == 0: 

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

if b 

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

if bool(b) 

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

другой ленивый способ я нашел это:

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

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

>>> [chr(ord('c')+i) for i in {z,x,y}] 
['c', 'd', 'f'] 

буква, соответствующая номеру дается здесь chr(ord('c')+i), где chr и ord мосты между символами и цифровыми кодами.

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

+1

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

7

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

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

Предыдущее решение: Как заявил Martijn Питерс, правильный и быстрый, формат:

if 1 in {x, y, z}: 

Одна из основных проблем, которые не представляется, что вы хотите, чтобы ваш выходной список включал каждую букву после утверждения if.

Использование только советы Мартейн Питерс вы бы сейчас:

if 0 in {x, y, z}: 
    Mylist.append("c") 
elif 1 in {x, y, z}: 
    Mylist.append("d") 
... 

Проблема: Первое, если заявление будет возвращена истина, и вы никогда не получите на следующий Элиф заявление. Так что ваш список будет просто вернуться:

["c"] 

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

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

Это будет работать, но «если» вы знакомы с использованием словарей (посмотреть, что я там делал), вы можете очистить это, сделав первоначальный словарь, отображающий число на письма, которые вы хотите, затем просто используйте цикл «для»:

numToLetters = {0:"c", 1:"d", 2:"e", 3:"f"} 
for number in numToLetters: 
    if number in {x, y, z}: 
     Mylist.append(numToLetters[number]) 
0

Не слишком усложняйтесь, просто следуйте самым простым способом.

x = 0 
y = 1 
z = 3 

Mylist = [] 

if x == 0 or y==0 or z==0: 
    Mylist.append('c') 
elif x == 1 or y == 1 or z==1: 
    Mylist.append('d') 
elif x==2 or y==2 or z==2: 
    Mylist.append('e') 
elif x==3 or y==3 or z==3: 
    Mylist.append('f') 

Cheers!

+4

Проблема с этим подходом заключается в высокой вероятности создания опечатки. Именно это и произошло. 'elif x == 1 или y == 2 или z == 2:' => 'elif x == 2 или y == 2 или z == 2:' oops. Исправлено. –

5

Все превосходные ответы, представленные здесь, сосредоточены на специфических требованиях оригинального плаката и сосредоточены на решении 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 
1

One line soluti на:

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

Или:

mylist = ['cdef'[i] for i in range(4) if i in (x, y, z)] 
Смежные вопросы