Як перевірити кілька змінних на значення?


644

Я намагаюся зробити функцію, яка буде порівнювати декілька змінних з цілим числом і виводити рядок з трьох літер. Мені було цікаво, чи є спосіб перекласти це на 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в (tuple)

2
Коли ви хочете оцінити список висловлювань у будь-який / будь-який спосіб, ви можете використовувати any/ allфункції. Наприклад: 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. Ви можете легко написати неправильний код, який дає "правильну" відповідь.
smci

Відповіді:


850

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

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

використовуючи a,set щоб скористатися тестом на членство з постійною вартістю ( inзаймає фіксовану кількість часу, незалежно від того, на якому лівому операнді знаходиться).

Під час використання orpython бачить кожну сторону оператора як окремі вирази. Вираз x or y == 1трактується як спочатку булевий тест для x, потім, якщо це помилково, вираз y == 1перевіряється.

Це пов'язано з перевагою оператора . orОператор має більш низький пріоритет , ніж ==тест, так що останній оцінюється першим .

Однак навіть якби це не було, а вираз x or y or z == 1насправді трактувався як (x or y or z) == 1натомість, це все одно не буде робити те, що ви очікуєте від цього.

x or y or zбуло б оцінено до першого аргументу, який є "truthy", наприклад, ні 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 кешує набір з них, щоб уникнути пом'якшення пам'яті, що робить найбільшу різницю в наборах тут.
Martijn Pieters

13
@dequestarmappartialsetattr: Якщо ви лише час тесту на членство, цілі набори та кортежі однаково швидкі для ідеального сценарію; відповідність першому елементу. Після цього кортежі програють набору.
Martijn Pieters

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

9
@ShadowRanger: так, вічко оптимізація (будь то in [...]або in {...}) працює тільки , якщо вміст списку або набору непорушні літерали теж.
Martijn Pieters

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 порядок виводу не визначається діктом, він визначається порядком списку[x, y, z]
dansalmo

1
Окрім розуміння списку, до якого я ще не повністю звик, більшість із нас мали той самий рефлекс: побудуйте цей дикт!
LoneWanderer

66

Як заявив Мартійн Пітерс, правильний і найшвидший формат:

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

Це буде працювати, але якщо вам зручно користуватися словниками (дивіться, що я там робив), ви можете це очистити, зробивши початковий словник, зіставляючи номери з потрібними літерами, а потім просто за допомогою циклу:

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)

BTW багато 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дає an AttributeError. Взагалі, я згоден з цим методом.
SethMMorton

2
dict замість ключа невірний, ви отримаєте 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 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]? Не використовуйте паралельні списки, використовуйте диктат. Загалом, це рішення набагато складніше, ніж рішення 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
Це не відповідає на питання ОП. Він охоплює лише перший випадок у наведеному прикладі.
wallacer

31

Тут встановлено хороший підхід, оскільки він впорядковує змінні, що, здається, є вашою ціллю тут. {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']

10

Рішення з однієї лінії:

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

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 (є 'c'):

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

Використовуйте математику, якщо формула 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))

...

З’єднайте ці формули, керуючись логікою:

  • логіка and - це сума квадратів рівнянь
  • логіка or- це добуток рівнянь

і у вас буде загальна сума рівняння експрес-суми, і ви маєте загальну формулу суми

тоді сума & 1 - c, сума & 2 - d, сума & 4 - е, сума & 5 - f

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

array[sum] дає вам рядок.



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.