Mengapa `a == b atau c atau d` selalu bernilai True?

108

Saya menulis sistem keamanan yang menolak akses ke pengguna yang tidak sah.

import sys

print("Hello. Please enter your name:")
name = sys.stdin.readline().strip()
if name == "Kevin" or "Jon" or "Inbar":
    print("Access granted.")
else:
    print("Access denied.")

Ini memberikan akses ke pengguna resmi seperti yang diharapkan, tetapi juga memungkinkan pengguna yang tidak sah!

Hello. Please enter your name:
Bob
Access granted.

Mengapa ini terjadi? Saya dengan jelas menyatakan untuk hanya memberikan akses jika namesetara dengan Kevin, Jon, atau Inbar. Saya juga mencoba logika yang berlawanan if "Kevin" or "Jon" or "Inbar" == name, tetapi hasilnya sama.

Kevin
sumber
1
@ Jean-François FYI ada beberapa diskusi tentang pertanyaan ini dan target penipuannya sebelumnya di ruang python, diskusi dimulai di sini . Saya mengerti jika Anda ingin menutupnya, tetapi saya pikir Anda mungkin ingin tahu tentang alasan postingan dibuka kembali baru-baru ini. Pengungkapan penuh: Martijn, penulis jawaban tentang target penipuan belum punya waktu untuk menanggapi masalah tersebut.
Andras Deak
Jawaban Martijn sangat bagus menjelaskannya dengan "jangan gunakan bahasa alami", yang lain, yah, ... itu adalah waktu upvoting yang luar biasa ... Jawaban di bawah ini hanya mengulangi ini. Bagi saya itu duplikat. Tetapi jika Martijn memilih untuk membuka kembali, saya tidak keberatan.
Jean-François Fabre
4
Variasi dari masalah ini meliputi x or y in z, x and y in z, x != y and zdan beberapa orang lainnya. Meskipun tidak persis sama dengan pertanyaan ini, akar penyebabnya sama untuk semuanya. Hanya ingin menunjukkan hal itu jika ada yang menjawab pertanyaan mereka sebagai duplikat dari ini dan tidak yakin bagaimana itu relevan bagi mereka.
Aran-Fey

Jawaban:

152

Dalam banyak kasus, Python terlihat dan berperilaku seperti bahasa Inggris alami, tetapi ini adalah salah satu kasus di mana abstraksi tersebut gagal. Orang dapat menggunakan petunjuk konteks untuk menentukan bahwa "Jon" dan "Inbar" adalah objek yang digabungkan dengan kata kerja "sama dengan", tetapi penerjemah Python lebih berpikiran literal.

if name == "Kevin" or "Jon" or "Inbar":

secara logis setara dengan:

if (name == "Kevin") or ("Jon") or ("Inbar"):

Yang, bagi pengguna Bob, setara dengan:

if (False) or ("Jon") or ("Inbar"):

The orOperator memilih argumen pertama dengan positif nilai kebenaran :

if ("Jon"):

Dan karena "Jon" memiliki nilai kebenaran positif, ifblok tersebut dijalankan. Itulah yang menyebabkan "Akses diberikan" untuk dicetak terlepas dari nama yang diberikan.

Semua alasan ini juga berlaku untuk ekspresi tersebut if "Kevin" or "Jon" or "Inbar" == name. nilai pertama,, "Kevin"benar, sehingga ifblok dijalankan.


Ada dua cara umum untuk menyusun persyaratan ini dengan benar.

  1. Gunakan beberapa ==operator untuk secara eksplisit memeriksa setiap nilai:
    if name == "Kevin" or name == "Jon" or name == "Inbar":

  2. Buat urutan nilai yang valid, dan gunakan inoperator untuk menguji keanggotaan:
    if name in {"Kevin", "Jon", "Inbar"}:

Secara umum dari dua yang kedua lebih disukai karena lebih mudah dibaca dan juga lebih cepat:

>>> import timeit
>>> timeit.timeit('name == "Kevin" or name == "Jon" or name == "Inbar"', setup="name='Inbar'")
0.4247764749999945
>>> timeit.timeit('name in {"Kevin", "Jon", "Inbar"}', setup="name='Inbar'")
0.18493307199999265

Bagi yang mungkin menginginkan pembuktian yang if a == b or c or d or e: ...memang diurai seperti ini. astModul built-in memberikan jawaban:

>>> import ast
>>> ast.parse("if a == b or c or d or e: ...")
<_ast.Module object at 0x1031ae6a0>
>>> ast.dump(_)
"Module(body=[If(test=BoolOp(op=Or(), values=[Compare(left=Name(id='a', ctx=Load()), ops=[Eq()], comparators=[Name(id='b', ctx=Load())]), Name(id='c', ctx=Load()), Name(id='d', ctx=Load()), Name(id='e', ctx=Load())]), body=[Expr(value=Ellipsis())], orelse=[])])"
>>>

Sehingga test dari ifpenampilan pernyataan seperti ini:

BoolOp(
 op=Or(),
 values=[
  Compare(
   left=Name(id='a', ctx=Load()),
   ops=[Eq()],
   comparators=[Name(id='b', ctx=Load())]
  ),
  Name(id='c', ctx=Load()),
  Name(id='d', ctx=Load()),
  Name(id='e', ctx=Load())
 ]
)

Seperti yang dapat dilihat, itu operator boolean orditerapkan untuk beberapa values, yaitu, a == bdan c, d, dan e.

Kevin
sumber
Apakah ada alasan khusus untuk memilih tupel ("Kevin", "Jon", "Inbar")daripada satu set {"Kevin", "Jon", "Inbar"} ?
Manusia
2
Tidak juga, karena keduanya berfungsi jika semua nilainya dapat di-hash. Pengujian keanggotaan himpunan memiliki kompleksitas big-O yang lebih baik daripada pengujian keanggotaan tupel, tetapi membangun himpunan sedikit lebih mahal daripada membuat tupel. Saya pikir ini sebagian besar merupakan pencucian untuk koleksi kecil seperti ini. Bermain-main dengan waktu, a in {b, c, d}kira-kira dua kali lebih cepat a in (b, c, d)dari mesin saya. Sesuatu untuk dipikirkan jika ini adalah bagian kode yang sangat penting untuk kinerja.
Kevin
3
Tuple atau daftar saat menggunakan 'in' dalam klausa 'if'? merekomendasikan set literal untuk pengujian keanggotaan. Saya akan memperbarui posting saya.
Kevin
Dalam Python modern, ia mengenali bahwa himpunan adalah sebuah konstanta dan menjadikannya sebagai frozensetgantinya, sehingga overhead himpunan konstruksi tidak ada. dis.dis(compile("1 in {1, 2, 3}", '<stdin>', 'eval'))
endolith
1

Masalah teknik sederhana, mari kita bahas lebih jauh.

In [1]: a,b,c,d=1,2,3,4
In [2]: a==b
Out[2]: False

Tapi, diwarisi dari bahasa C, Python mengevaluasi nilai logis dari bilangan bulat bukan nol sebagai True.

In [11]: if 3:
    ...:     print ("yey")
    ...:
yey

Sekarang, Python membangun logika itu dan membiarkan Anda menggunakan literal logika seperti atau pada bilangan bulat, dan sebagainya

In [9]: False or 3
Out[9]: 3

Akhirnya

In [4]: a==b or c or d
Out[4]: 3

Cara yang tepat untuk menulisnya adalah:

In [13]: if a in (b,c,d):
    ...:     print('Access granted')

Demi keamanan, saya juga menyarankan agar Anda tidak memasukkan sandi kode keras.

pengguna1854182
sumber
1

Ada 3 kondisi check in if name == "Kevin" or "Jon" or "Inbar":

  • name == "Kevin"
  • "Jon"
  • "Inbar"

dan pernyataan if ini setara dengan

if name == "Kevin":
    print("Access granted.")
elif "Jon":
    print("Access granted.")
elif "Inbar":
    print("Access granted.")
else:
    print("Access denied.")

Karena elif "Jon"akan selalu benar sehingga akses ke setiap pengguna diberikan

Larutan


Anda dapat menggunakan salah satu metode di bawah ini

Cepat

if name in ["Kevin", "Jon", "Inbar"]:
    print("Access granted.")
else:
    print("Access denied.")

Lambat

if name == "Kevin" or name == "Jon" or name == "Inbar":
    print("Access granted.")
else:
    print("Access denied.")

Lambat + Kode yang tidak perlu

if name == "Kevin":
    print("Access granted.")
elif name == "Jon":
    print("Access granted.")
elif name == "Inbar":
    print("Access granted.")
else:
    print("Access denied.")
7u5h4r
sumber