Jak przetestować wiele zmiennych względem wartości?

644

Próbuję stworzyć funkcję, która porówna wiele zmiennych do liczby całkowitej i wyśle ​​ciąg trzech liter. Zastanawiałem się, czy istnieje sposób na przetłumaczenie tego na Python. Powiedz więc:

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

co zwróci listę:

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

Czy coś takiego jest możliwe?

użytkownik1877442
źródło
5
use 1in (krotka)
2
Jeśli chcesz ocenić listę instrukcji w dowolny sposób, możesz użyć any/ allfunkcji. Na przykład: all([1, 2, 3, 4, False])zwróci Fałsz all([True, 1, 2, 3])zwróci Prawda any([False, 0, 0, False])zwróci Fałsz any([False, 0, True, False])zwróci Prawda
eddd
4
To pytanie jest bardzo popularnym duplikatem celu, ale myślę, że jest w tym celu nieoptymalne. Większość ludzi próbuje zrobić coś takiego if x == 0 or 1:, co oczywiście jest podobne if x or y == 0:, ale może być nieco mylące dla początkujących. Biorąc pod uwagę czysty tom „Dlaczego moja x == 0 or 1praca nie działa?” pytań, wolałbym raczej użyć tego pytania jako naszego kanonicznego duplikatu tych pytań.
Aran-Fey
1
Należy zachować szczególną ostrożność przy porównywaniu do wartości „falsey” jak 0, 0.0lub False. Możesz łatwo napisać zły kod, który daje „właściwą” odpowiedź.
smci,

Odpowiedzi:

850

Nie rozumiesz, jak działają wyrażenia logiczne; nie działają jak angielskie zdanie i domyślają się, że mówisz o tym samym porównaniu dla wszystkich nazw tutaj. Szukasz:

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

xi ysą inaczej oceniane na własną rękę ( Falsejeśli 0, Trueinaczej).

Możesz to skrócić za pomocą testu powstrzymywania krotki :

if 1 in (x, y, z):

lub jeszcze lepiej:

if 1 in {x, y, z}:

używając a,set aby skorzystać z testu członkostwa o stałych kosztach ( inzajmuje to określoną ilość czasu bez względu na operand po lewej stronie).

Gdy używasz or, python widzi każdą stronę operatora jako osobne wyrażenie. Wyrażenie x or y == 1jest traktowane jako najpierw test boolowski x, a następnie, jeśli jest to Fałsz, wyrażenie y == 1jest testowane.

Wynika to z pierwszeństwa operatora . orOperator ma niższy priorytet niż ==testu więc jest ona oceniana pierwszy .

Jednak nawet gdyby tak nie było, a wyrażenie x or y or z == 1zostało zinterpretowane w (x or y or z) == 1ten sposób, zamiast tego nadal nie działałoby zgodnie z oczekiwaniami.

x or y or zocenia pierwszy argument, który jest „prawdziwy”, np. nie False, 0 lub pusty (zobacz wyrażenia logiczne, aby uzyskać szczegółowe informacje na temat tego, co Python uważa za fałszywy w kontekście boolowskim).

Więc dla wartości x = 2; y = 1; z = 0, x or y or zrozwiąże się 2, ponieważ jest to pierwszy prawdziwy podobną wartość w argumentach. Wtedy 2 == 1byłoby False, chociaż y == 1byłoby True.

To samo dotyczyłoby odwrotności; testowanie wielu wartości względem jednej zmiennej; x == 1 or 2 or 3zawiedzie z tych samych powodów. Użyj x == 1 or x == 2 or x == 3lub x in {1, 2, 3}.

Martijn Pieters
źródło
116
Nie byłbym tak szybki na setwersję. Krotki są bardzo tanie w tworzeniu i powtarzaniu. Na mojej maszynie krotki są szybsze niż zestawy, o ile rozmiar krotki wynosi około 4-8 elementów. Jeśli musisz zeskanować więcej, użyj zestawu, ale jeśli szukasz przedmiotu z 2-4 możliwości, krotka jest jeszcze szybsza! Jeśli można zorganizować najbardziej prawdopodobnym przypadku być pierwszym w krotce, wygrana jest jeszcze większa: (moje testy: timeit.timeit('0 in {seq}'.format(seq=tuple(range(9, -1, -1)))))
SingleNegationElimination
57
@dequestarmappartialsetattr: W Pythonie 3.3 i nowszych zestaw jest przechowywany jako stała, pomijając całkowity czas tworzenia, eliminując czas tworzenia. Krotki mogą być tanie w tworzeniu, ponieważ Python buforuje ich pakiet, aby uniknąć utraty pamięci, co stanowi największą różnicę w zestawach tutaj.
Martijn Pieters
13
@dequestarmappartialsetattr: Jeśli czas tylko test członkostwa, dla liczb całkowitych i krotek są równie szybkie dla idealnego scenariusza; dopasowanie pierwszego elementu. Po tym krotki przegrywają z setami.
Martijn Pieters
17
@MartijnPieters: Używanie setnotacji literalnej w tym teście nie stanowi oszczędności, chyba że zawartość setliterału jest również literalna, prawda? if 1 in {x, y, z}:Nie można buforować set, ponieważ x, yi zmoże się zmienić, więc albo potrzeby rozwiązanie do budowy tuplelub setod podstaw, i podejrzewam, że cokolwiek odnośnika oszczędności można uzyskać podczas sprawdzania członkostwa będzie zapchana przez większy setczas tworzenia.
ShadowRanger
9
@ShadowRanger: tak, optymalizacja wizjera (czy to dla in [...]czy in {...}) działa tylko wtedy, gdy zawartość listy lub zestawu jest również niezmiennymi literałami.
Martijn Pieters
96

Twój problem można łatwiej rozwiązać za pomocą struktury słownika, takiej jak:

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
źródło
21
Lub nawet d = "cdef"co prowadzi doMyList = ["cdef"[k] for k in [x, y, z]]
aragaer
9
lubmap(lambda i: 'cdef'[i], [x, y, z])
dansalmo
3
@MJM kolejność wyjścia nie jest określona przez dict, to zależy od kolejności na liście[x, y, z]
dansalmo
1
Oprócz zrozumienia listy, do której jeszcze nie jestem w pełni przyzwyczajony, większość z nas miała ten sam odruch: zbuduj ten dykt!
LoneWanderer
66

Jak stwierdził Martijn Pieters, poprawny i najszybszy format to:

if 1 in {x, y, z}:

Korzystając z jego rad, będziesz teraz mieć osobne instrukcje if, aby Python odczytał każdą instrukcję, niezależnie od tego, czy były to pierwsze, Trueczy False. Jak na przykład:

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

To zadziała, ale jeśli nie masz nic przeciwko korzystaniu ze słowników (zobacz, co tam zrobiłem), możesz to wyczyścić, tworząc początkowy słownik odwzorowujący liczby na pożądane litery, a następnie używając pętli 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
źródło
45

Bezpośrednim sposobem pisania x or y or z == 0jest

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

Ale nie sądzę, że ci się podoba. :) A ta droga jest brzydka.

Drugi sposób (lepszy) to:

0 in (x, y, z)

BTW wiele ifs można zapisać jako coś takiego

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
źródło
8
W twoim przykładzie dictzamiast klucza będziesz otrzymywać błędy, ponieważ zwracaną wartością .appendjest None, a wywołanie Nonedaje AttributeError. Ogólnie jednak zgadzam się z tą metodą.
SethMMorton
2
dykt zamiast klucza jest nieprawidłowy, otrzymasz Mylist = ['c', 'd'], gdy słownik zostanie zainicjowany, nawet jeśli skomentowałeś część „for..loop”
Mahmoud Elshahat
1
W twoim pierwszym przykładzie filterbyłoby lepiej niż map, ponieważ zwróci tylko te przypadki, w których lambda ocenia się jako prawda
Alex
1
any(v == 0 for v in (x, y, z))
Zrozumienie
35

Jeśli jesteś bardzo leniwy, możesz umieścić wartości w tablicy. Jak na przykład

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

Możesz także wstawić cyfry i litery do słownika i zrobić to, ale jest to prawdopodobnie dużo bardziej skomplikowane niż zwykłe instrukcje. Właśnie to dostajesz za próbę bycia wyjątkowo leniwym :)

Jeszcze jedno, twoje

if x or y or z == 0:

będzie się kompilować, ale nie w sposób, w jaki chcesz. Gdy po prostu wstawisz zmienną do instrukcji if (przykład)

if b

program sprawdzi, czy zmienna nie ma wartości null. Innym sposobem napisania powyższej instrukcji (co ma większy sens) jest

if bool(b)

Bool to wbudowana funkcja w pythonie, która w zasadzie wykonuje polecenie weryfikacji instrukcji boolowskiej (jeśli nie wiesz, co to jest, właśnie to próbujesz wykonać w instrukcji if :))

Znalazłem inny leniwy sposób:

if any([x==0, y==0, z==0])
ytpillai
źródło
3
-1 Tutaj jest wiele złych praktyk. listjest wbudowanym Pythonem; zamiast tego użyj innej nazwy, xyzna przykład. Dlaczego konstruujesz listę w czterech krokach, kiedy możesz to zrobić, tj. xyz = [x, y, z]? Nie używaj list równoległych, zamiast tego użyj dykta. Podsumowując, to rozwiązanie jest znacznie bardziej skomplikowane niż rozwiązanie ThatGuyRussell . Także w ostatniej części, dlaczego nie zrobić zrozumienia, tj. any(v == 0 for v in (x, y, z))? Również tablice są czymś innym w Pythonie.
wjandrea
32

Aby sprawdzić, czy wartość jest zawarta w zestawie zmiennych, możesz użyć wbudowanych modułów itertoolsioperator .

Na przykład:

Import:

from itertools import repeat
from operator import contains

Deklaruj zmienne:

x = 0
y = 1
z = 3

Utwórz mapowanie wartości (w kolejności, którą chcesz sprawdzić):

check_values = (0, 1, 3)

Użyj, itertoolsaby umożliwić powtórzenie zmiennych:

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

Na koniec użyj mapfunkcji, aby utworzyć iterator:

checker = map(contains, check_vars, check_values)

Następnie, sprawdzając wartości (w oryginalnej kolejności), użyj next():

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

itp...

Ma to przewagę nad, lambda x: x in (variables)ponieważ operatorjest wbudowanym modułem i jest szybsze i bardziej wydajne niż używanielambda które musi tworzyć niestandardowe funkcje na miejscu.

Inna opcja sprawdzania, czy na liście jest niezerowa (lub Fałsz) wartość:

not (x and y and z)

Odpowiednik:

not all((x, y, z))
GuiltyDolphin
źródło
2
To nie odpowiada na pytanie PO. Dotyczy tylko pierwszego przypadku w podanym przykładzie.
wallacer
31

Set jest tutaj dobrym podejściem, ponieważ porządkuje zmienne, co wydaje się twoim celem tutaj. {z,y,x}jest {0,1,3}niezależnie od kolejności parametrów.

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

W ten sposób całe rozwiązanie to O (n).

BM
źródło
5
Powinieneś dodać opis tego, co osiąga Twój kod i jak to robi. Krótkie odpowiedzi przy użyciu
samego
31

Wszystkie podane tutaj doskonałe odpowiedzi koncentrują się na specyficznych wymaganiach oryginalnego plakatu i koncentrują się na if 1 in {x,y,z}rozwiązaniu zaproponowanym przez Martijn Pieters.
To, co ignorują, to szersza implikacja pytania:
jak przetestować jedną zmienną pod kątem wielu wartości?
Podane rozwiązanie nie będzie działać w przypadku częściowych trafień, jeśli na przykład użyjesz ciągów znaków:
Sprawdź, czy ciąg „Wild” ma wiele wartości

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

lub

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

w tym scenariuszu najłatwiej jest przekonwertować na ciąg

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

Należy jednak zauważyć, jak wspomniano przez @codeforester , że granice słów są tracone za pomocą tej metody, jak w:

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

3 litery rotistnieją w połączeniu na liście, ale nie jako pojedyncze słowo. Testowanie na „zgniliznę” nie powiodło się, ale jeśli jeden z elementów listy byłby „zgnilizny w piekle”, również to by się nie powiodło.
W związku z tym, jeśli używasz tej metody, uważaj na kryteria wyszukiwania i pamiętaj, że ma to ograniczenie.

Rolf z Saksonii
źródło
30

Myślę, że to poradzi sobie lepiej:

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]

Wynik:

print validate(0, 8, 9)
c
print validate(9, 8, 9)
None
print validate(9, 8, 2)
e
Bhargav Boda
źródło
30

Jeśli chcesz użyć if, w innym przypadku poniższe rozwiązanie jest innym rozwiązaniem:

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
źródło
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
źródło
26

Ten kod może być pomocny

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
źródło
12

Możesz wypróbować metodę pokazaną poniżej. W tej metodzie będziesz mieć swobodę określania / wprowadzania liczby zmiennych, które chcesz wprowadzić.

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
źródło
10

Rozwiązanie jednoliniowe:

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

Lub:

mylist = ['cdef'[i] for i in range(4) if i in (x, y, z)]
Vinayak Kaniyarakkal
źródło
9

Może potrzebujesz bezpośredniej formuły dla zestawu bitów wyjściowych.

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

Mapujmy na bity: 'c':1 'd':0xb10 'e':0xb100 'f':0xb1000

Relacja isc (jest „c”):

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

Użyj matematyki, jeśli formuła https://youtu.be/KAdKCgBGK0k?list=PLnI9xbPdZUAmUL8htSl6vToPQRRN3hhFp&t=315

[do]: (xyz=0 and isc=1) or (((xyz=0 and isc=1) or (isc=0)) and (isc=0))

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

...

Połącz te formuły według następującej logiki:

  • logika andjest sumą kwadratów równań
  • logika orjest iloczynem równań

a otrzymasz sumę równania wyrażoną sumą i masz całkowitą formułę sumy

następnie suma i 1 to c, suma i 2 to d, suma i 4 to e, suma i 5 to f

Następnie możesz utworzyć predefiniowaną tablicę, w której indeks elementów łańcucha odpowiadałby gotowemu łańcuchowi.

array[sum] daje ci ciąg.

Siergiej
źródło
7

Można to łatwo zrobić jako

for value in [var1,var2,var3]:
     li.append("targetValue")
Seenivasan
źródło
6

Najbardziej mnemonicznym sposobem reprezentowania twojego pseudokodu w Pythonie byłoby:

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
źródło
1
To podejście jest bardziej uniwersalne niż `if 2 in (x, y, z): mylist.append ('e')`, ponieważ pozwala na dowolne porównania (np if any(v >= 42 for v in (x, y, z)):.). I wydajność wszystkich 3 metod ( 2 in {x,y,z}, 2 in (x,y,z), any(_v == 2 for _v in (x,y,z))) wydaje się być prawie takie same w CPython3.6 (patrz GIST )
imposeren
5

Aby przetestować wiele zmiennych za pomocą jednej wartości: if 1 in {a,b,c}:

Aby przetestować wiele wartości za pomocą jednej zmiennej: if a in {1, 2, 3}:

alamin
źródło
4

Wygląda na to, że budujesz jakiś szyfr Cezara.

O wiele bardziej ogólne podejście to:

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

wyjścia

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

Nie jestem pewien, czy jest to pożądany efekt uboczny kodu, ale kolejność wyników zawsze będzie sortowana.

Jeśli tego właśnie chcesz, ostatnią linię można zmienić na:

sorted([chr(val + origo) for val in inputs])
firelynx
źródło
2

Możesz użyć słownika:

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
źródło
1
To może się przydać więcej niż raz. Zestaw?
Siergiej
2

Bez dyktowania wypróbuj to rozwiązanie:

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

i daje:

['c', 'd', 'f']
Massifox
źródło
0

To ci pomoże.

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
źródło
0

Możesz to zjednoczyć

x = 0
y = 1
z = 3

w jednej zmiennej.

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

Zmień nasze warunki jako:

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

Wynik:

In [21]: mylist                                                                                
Out[21]: ['c', 'd', 'f']
Serhii
źródło
0

Problem

Podczas gdy wzorzec do testowania wielu wartości

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

jest bardzo czytelny i działa w wielu sytuacjach, istnieje jedna pułapka:

>>> 0 in {True, False}
True

Ale my chcemy mieć

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

Rozwiązanie

Jedno uogólnienie poprzedniego wyrażenia opiera się na odpowiedzi z ytpillai :

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

które można zapisać jako

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

Chociaż to wyrażenie zwraca właściwy wynik, nie jest tak czytelne jak pierwsze wyrażenie :-(

FHGD
źródło