Bagaimana cara menguji beberapa variabel terhadap suatu nilai?

645

Saya mencoba membuat fungsi yang akan membandingkan banyak variabel dengan integer dan menghasilkan string tiga huruf. Saya bertanya-tanya apakah ada cara untuk menerjemahkan ini ke Python. Jadi katakan:

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

yang akan mengembalikan daftar:

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

Apakah hal seperti ini mungkin?

pengguna1877442
sumber
5
gunakan 1dalam (tuple)
2
Saat Anda ingin mengevaluasi daftar pernyataan dengan cara apa pun yang dapat Anda gunakan any/ allfungsi. Sebagai contoh: all([1, 2, 3, 4, False])akan mengembalikan False all([True, 1, 2, 3])akan kembali True any([False, 0, 0, False])akan kembali False any([False, 0, True, False])akan kembali Benar
eddd
4
Pertanyaan ini adalah target duplikat yang sangat populer, tapi saya pikir itu kurang optimal untuk tujuan itu. Kebanyakan orang mencoba melakukan sesuatu seperti if x == 0 or 1:, yang tentu saja mirip if x or y == 0:, tetapi mungkin sedikit membingungkan bagi pemula. Mengingat volume tipis "Mengapa saya tidak x == 0 or 1bekerja?" pertanyaan, saya lebih suka menggunakan pertanyaan ini sebagai target duplikat kanonik kami untuk pertanyaan ini.
Aran-Fey
1
Berhati-hatilah saat membandingkan nilai "falsey" seperti 0, 0.0atau False. Anda dapat dengan mudah menulis kode yang salah yang memberikan jawaban "benar".
smci
1
Untuk kebalikannya, lihat Membandingkan string ke beberapa item dalam Python
tripleee

Jawaban:

850

Anda salah mengerti bagaimana ekspresi boolean bekerja; mereka tidak bekerja seperti kalimat bahasa Inggris dan kira Anda berbicara tentang perbandingan yang sama untuk semua nama di sini. Anda mencari:

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

xdan ysebaliknya dievaluasi sendiri ( Falsejika 0, Truesebaliknya).

Anda dapat mempersingkat hal itu menggunakan uji penahanan terhadap tuple :

if 1 in (x, y, z):

atau lebih baik lagi:

if 1 in {x, y, z}:

menggunakan sebuahset untuk mengambil keuntungan dari tes keanggotaan konstan-biaya ( inmengambil jumlah waktu yang tetap apapun kiri operan adalah).

Saat Anda menggunakan or, python melihat setiap sisi operator sebagai ekspresi terpisah . Ekspresi x or y == 1diperlakukan sebagai tes boolean pertama untuk x, maka jika itu Salah, ekspresi y == 1diuji.

Ini karena prioritas operator . The oroperator memiliki prioritas lebih rendah dari ==tes, jadi yang terakhir dievaluasi pertama .

Namun, bahkan jika ini bukan masalahnya, dan ekspresi x or y or z == 1sebenarnya ditafsirkan sebagai (x or y or z) == 1gantinya, ini masih tidak akan melakukan apa yang Anda harapkan.

x or y or zakan mengevaluasi argumen pertama yang 'benar', misalnya tidak False, angka 0 atau kosong (lihat ekspresi boolean untuk detail tentang apa yang Python anggap salah dalam konteks boolean).

Jadi untuk nilai-nilai x = 2; y = 1; z = 0, x or y or zakan memutuskan untuk 2, karena itu adalah nilai true-like pertama dalam argumen. Maka 2 == 1akan menjadi False, meskipun y == 1akan True.

Hal yang sama berlaku untuk kebalikannya; menguji beberapa nilai terhadap satu variabel; x == 1 or 2 or 3akan gagal karena alasan yang sama. Gunakan x == 1 or x == 2 or x == 3atau x in {1, 2, 3}.

Martijn Pieters
sumber
116
Saya tidak akan begitu cepat untuk setversi. Tuple's sangat murah untuk dibuat dan digunakan kembali. Pada mesin saya setidaknya, tuple lebih cepat daripada set asalkan ukuran tuple sekitar 4-8 elemen. Jika Anda harus memindai lebih dari itu, gunakan satu set, tetapi jika Anda mencari item dari 2-4 kemungkinan, tuple masih lebih cepat! Jika Anda dapat mengatur untuk yang paling mungkin kasus menjadi yang pertama dalam tupel, menang bahkan lebih besar: (pengujian saya: timeit.timeit('0 in {seq}'.format(seq=tuple(range(9, -1, -1)))))
SingleNegationElimination
57
@dequestarmappartialsetattr: Dalam Python 3.3 dan lebih tinggi, set disimpan sebagai konstanta, melewati waktu pembuatan sama sekali, menghilangkan waktu pembuatan. Tuples bisa murah untuk dibuat karena Python melakukan cache bundel untuk menghindari memory churn, menjadikannya perbedaan terbesar dengan set di sini.
Martijn Pieters
13
@dequestarmappartialsetattr: Jika Anda hanya menguji waktu keanggotaan, untuk set bilangan bulat dan tupel sama-sama cepat untuk skenario ideal; cocok dengan elemen pertama. Setelah itu tuple kalah dari set.
Martijn Pieters
17
@ MartijnPieters: Menggunakan setnotasi literal untuk tes ini bukan penghematan kecuali isi setliteral juga literal, bukan? if 1 in {x, y, z}:tidak dapat men-cache set, karena x, ydan zbisa berubah, jadi salah satu solusi perlu membangun tupleatau setdari awal, dan saya curiga penghematan pencarian apa pun yang mungkin Anda dapatkan ketika memeriksa keanggotaan akan dibanjiri oleh setwaktu pembuatan yang lebih besar .
ShadowRanger
9
@ShadowRanger: ya, optimisasi lubang intip (baik untuk in [...]atau in {...}) hanya berfungsi jika konten daftar atau set juga literal yang tidak dapat diubah.
Martijn Pieters
96

Masalah Anda lebih mudah diatasi dengan struktur kamus seperti:

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
sumber
21
Atau bahkan d = "cdef"yang mengarah keMyList = ["cdef"[k] for k in [x, y, z]]
aragaer
9
ataumap(lambda i: 'cdef'[i], [x, y, z])
dansalmo
3
@ MJM urutan output tidak ditentukan oleh dikt, itu ditentukan oleh urutan daftar[x, y, z]
dansalmo
1
Selain dari pemahaman daftar yang saya belum terbiasa sepenuhnya, kebanyakan dari kita memiliki refleks yang sama: membangun dict itu!
LoneWanderer
66

Sebagaimana dinyatakan oleh Martijn Pieters, format yang benar dan tercepat adalah:

if 1 in {x, y, z}:

Menggunakan sarannya Anda sekarang akan memiliki pernyataan if terpisah sehingga Python akan membaca setiap pernyataan apakah pernyataan sebelumnya adalah Trueatau False. Seperti:

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

Ini akan berhasil, tetapi jika Anda merasa nyaman menggunakan kamus (lihat apa yang saya lakukan di sana), Anda dapat membersihkan ini dengan membuat kamus awal yang memetakan angka ke huruf yang Anda inginkan, kemudian hanya menggunakan for-loop:

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
sumber
45

Cara langsung untuk menulis x or y or z == 0adalah

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

Tapi saya tidak berpikir, Anda menyukainya. :) Dan cara ini jelek.

Cara lain (yang lebih baik) adalah:

0 in (x, y, z)

Banyak BTW ifs dapat ditulis sebagai sesuatu seperti ini

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
sumber
8
Dalam contoh Anda dictalih - alih kunci, Anda akan mendapatkan kesalahan karena nilai baliknya .appendadalah None, dan panggilan Nonememberi sebuah AttributeError. Secara umum saya setuju dengan metode ini.
SethMMorton
2
dikt bukannya kunci salah, Anda akan mendapatkan Mylist = ['c', 'd'] ketika kamus diinisialisasi bahkan jika Anda berkomentar "untuk..loop"
Mahmoud Elshahat
1
Dalam contoh pertama Anda filterakan lebih baik daripada map, karena hanya akan mengembalikan contoh di mana lambda menilai true
Alex
1
Pemahaman jauh lebih sederhana daripada peta lambda:any(v == 0 for v in (x, y, z))
wjandrea
35

Jika Anda sangat malas, Anda bisa meletakkan nilai-nilai di dalam array. Seperti

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

Anda juga dapat memasukkan angka dan huruf ke dalam kamus dan melakukannya, tetapi ini mungkin BANYAK lebih rumit daripada sekadar pernyataan. Itulah yang Anda dapatkan karena berusaha menjadi sangat malas :)

Satu hal lagi, milikmu

if x or y or z == 0:

akan dikompilasi, tetapi tidak dengan cara yang Anda inginkan. Ketika Anda cukup meletakkan variabel dalam pernyataan if (contoh)

if b

program akan memeriksa apakah variabelnya bukan nol. Cara lain untuk menulis pernyataan di atas (yang lebih masuk akal) adalah

if bool(b)

Bool adalah fungsi inbuilt dalam python yang pada dasarnya melakukan perintah memverifikasi pernyataan boolean (Jika Anda tidak tahu apa itu, itu adalah apa yang Anda coba buat dalam pernyataan if Anda sekarang :))

Cara malas lain yang saya temukan adalah:

if any([x==0, y==0, z==0])
ytpillai
sumber
3
-1 Ada banyak praktik buruk di sini. listadalah built-in Python; gunakan nama lain saja, seperti xyzmisalnya. Mengapa Anda membuat daftar dalam empat langkah ketika Anda bisa melakukannya, yaitu xyz = [x, y, z]? Jangan gunakan daftar paralel, gunakan dict sebagai gantinya. Secara keseluruhan, solusi ini jauh lebih berbelit-belit daripada milik ThatGuyRussell . Juga untuk bagian terakhir, mengapa tidak melakukan pemahaman, yaitu any(v == 0 for v in (x, y, z))? Juga array adalah sesuatu yang lain di Python.
wjandrea
32

Untuk memeriksa apakah suatu nilai terkandung dalam seperangkat variabel, Anda dapat menggunakan modul inbuilt itertoolsdan operator.

Sebagai contoh:

Impor:

from itertools import repeat
from operator import contains

Deklarasikan variabel:

x = 0
y = 1
z = 3

Buat pemetaan nilai (sesuai urutan yang ingin Anda periksa):

check_values = (0, 1, 3)

Gunakan itertoolsuntuk memungkinkan pengulangan variabel:

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

Akhirnya, gunakan mapfungsi untuk membuat iterator:

checker = map(contains, check_vars, check_values)

Kemudian, ketika memeriksa nilai-nilai (dalam urutan asli), gunakan next():

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

dll ...

Ini memiliki keunggulan dibandingkan lambda x: x in (variables)karena operatormodul inbuilt dan lebih cepat dan lebih efisien daripada menggunakan lambdayang harus membuat fungsi kustom di tempat.

Opsi lain untuk memeriksa apakah ada nilai bukan nol (atau salah) dalam daftar:

not (x and y and z)

Setara:

not all((x, y, z))
GuiltyDolphin
sumber
2
Ini tidak menjawab pertanyaan OP. Ini hanya mencakup kasus pertama dalam contoh yang diberikan.
wallacer
31

Set adalah pendekatan yang baik di sini, karena memerintahkan variabel, apa yang tampaknya menjadi tujuan Anda di sini. {z,y,x}adalah {0,1,3}apa urutan parameter.

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

Dengan cara ini, seluruh solusi adalah O (n).

BM
sumber
5
Anda harus menambahkan deskripsi tentang apa yang dicapai kode Anda dan bagaimana melakukannya. Jawaban singkat menggunakan kode saja tidak disarankan
Raniz
31

Semua jawaban bagus yang disediakan di sini berkonsentrasi pada persyaratan spesifik dari poster asli dan berkonsentrasi pada if 1 in {x,y,z}solusi yang diajukan oleh Martijn Pieters.
Apa yang mereka abaikan adalah implikasi yang lebih luas dari pertanyaan:
Bagaimana cara menguji satu variabel terhadap beberapa nilai?
Solusi yang diberikan tidak akan berfungsi untuk hit parsial jika menggunakan string misalnya:
Uji apakah string "Wild" berada dalam beberapa nilai

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

atau

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

untuk skenario ini, paling mudah mengkonversi ke 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

Namun perlu dicatat, sebagaimana disebutkan oleh @codeforester, bahwa batasan kata hilang dengan metode ini, seperti pada:

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

3 huruf rotmemang ada dalam kombinasi dalam daftar tetapi tidak sebagai kata individual. Menguji "busuk" akan gagal tetapi jika salah satu item daftar "membusuk di neraka", itu akan gagal juga.
Hasilnya, berhati-hatilah dengan kriteria pencarian Anda jika menggunakan metode ini dan ketahuilah bahwa itu memang memiliki batasan ini.

Rolf of Saxony
sumber
30

Saya pikir ini akan menanganinya dengan lebih baik:

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]

Keluaran:

print validate(0, 8, 9)
c
print validate(9, 8, 9)
None
print validate(9, 8, 2)
e
Bhargav Boda
sumber
30

Jika Anda ingin menggunakan if, pernyataan lain berikut ini adalah solusi lain:

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
sumber
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
sumber
26

Kode ini mungkin bermanfaat

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
sumber
12

Anda dapat mencoba metode yang ditunjukkan di bawah ini. Dalam metode ini, Anda akan memiliki kebebasan untuk menentukan / memasukkan jumlah variabel yang ingin Anda masukkan.

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
sumber
10

Solusi satu baris:

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

Atau:

mylist = ['cdef'[i] for i in range(4) if i in (x, y, z)]
Vinayak Kaniyarakkal
sumber
9

Mungkin Anda perlu rumus langsung untuk bit output yang ditetapkan.

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

Mari kita petakan ke bit: 'c':1 'd':0xb10 'e':0xb100 'f':0xb1000

Hubungan isc (is 'c'):

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

Gunakan matematika jika rumus 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))

...

Hubungkan formula ini dengan logika berikut:

  • logika andadalah jumlah kuadrat dari persamaan
  • Logika oradalah produk dari persamaan

dan Anda akan memiliki jumlah persamaan total express dan Anda memiliki total rumus jumlah

lalu jumlah & 1 adalah c, jumlah & 2 adalah d, jumlah & 4 adalah e, ​​jumlah & 5 adalah f

Setelah ini, Anda dapat membentuk array standar di mana indeks elemen string akan sesuai dengan string siap.

array[sum] memberi Anda string.

Sergei
sumber
7

Ini bisa dilakukan dengan mudah

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

Cara paling mnemonik untuk merepresentasikan kode semu Anda dengan Python adalah:

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
sumber
1
Pendekatan ini lebih universal daripada `jika 2 in (x, y, z): mylist.append ('e')` karena memungkinkan perbandingan sewenang-wenang (misalnya if any(v >= 42 for v in (x, y, z)):). Dan kinerja semua 3 metode ( 2 in {x,y,z}, 2 in (x,y,z), any(_v == 2 for _v in (x,y,z))) tampaknya hampir sama di CPython3.6 (lihat Intisari )
imposeren
5

Untuk menguji beberapa variabel dengan satu nilai tunggal: if 1 in {a,b,c}:

Untuk menguji beberapa nilai dengan satu variabel: if a in {1, 2, 3}:

alamin
sumber
4

Sepertinya Anda sedang membangun semacam cipher Caesar.

Pendekatan yang lebih umum adalah:

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

output

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

Tidak yakin apakah itu efek samping yang diinginkan dari kode Anda, tetapi urutan output Anda akan selalu diurutkan.

Jika ini yang Anda inginkan, baris terakhir dapat diubah menjadi:

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

Anda dapat menggunakan kamus:

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
sumber
1
Ini mungkin menambahkan sama lebih dari sekali ini. Set?
Sergei
2

Tanpa dikte, coba solusi ini:

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

dan memberi:

['c', 'd', 'f']
Massifox
sumber
0

Ini akan membantu Anda.

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
sumber
0

Anda bisa menyatukan ini

x = 0
y = 1
z = 3

dalam satu variabel.

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

Ubah kondisi kami sebagai:

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

Keluaran:

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

Masalah

Sedangkan pola untuk pengujian beberapa nilai

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

sangat mudah dibaca dan bekerja dalam banyak situasi, ada satu perangkap:

>>> 0 in {True, False}
True

Tapi kami ingin memilikinya

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

Larutan

Satu generalisasi dari ungkapan sebelumnya didasarkan pada jawaban dari ytpillai :

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

yang dapat ditulis sebagai

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

Meskipun ungkapan ini mengembalikan hasil yang tepat, itu tidak dapat dibaca seperti ungkapan pertama :-(

fhgd
sumber