Neden "a == b veya c veya d" her zaman Doğru olarak değerlendirilir?


110

Yetkisiz kullanıcıların erişimini engelleyen bir güvenlik sistemi yazıyorum.

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

Yetkili kullanıcılara beklendiği gibi erişim izni verir, ancak yetkisiz kullanıcılara da izin verir!

Hello. Please enter your name:
Bob
Access granted.

Bu neden oluyor? Sadece nameKevin, Jon veya Inbar'a eşit olduğunda erişim izni vermeyi açıkça belirttim . Ben de tam tersi mantığı denedim if "Kevin" or "Jon" or "Inbar" == nameama sonuç aynı.


1
@ Jean-François FYI Python odasında daha önce bu soru ve onun dupe hedefi hakkında bazı tartışmalar oldu, tartışma burada başlıyor . Kapatmak isteyip istemediğinizi anlıyorum, ancak gönderinin yakın zamanda yeniden açılma nedenlerini öğrenmek isteyebileceğinizi düşündüm. Tam açıklama: Dupe hedefle ilgili cevabın yazarı Martijn'in henüz konuya girecek zamanı olmadı.
Andras Deak

Martijn cevabı bunu "doğal dil kullanma" ile açıklamak için mükemmeldir, diğerleri, pekala, ... bunlar muhteşem oylama zamanlarıydı ... Aşağıdaki cevap sadece bunu tekrarlıyor. Benim için bir kopya. Ama Martijn yeniden açmayı seçerse, umurumda değil.
Jean-François Fabre

4
Bu sorunun Varyasyonları şunlardır x or y in z, x and y in z, x != y and zve birkaç başka. Bu soru ile tam olarak aynı olmasa da, temel neden hepsi için aynıdır. Sadece birisinin sorusunu bunun kopyası olarak kapatması ve bunun onlar için ne kadar önemli olduğundan emin olamaması durumunda bunu belirtmek istedim.
Aran-Fey

Yanıtlar:


155

Çoğu durumda, Python doğal İngilizce gibi görünür ve davranır, ancak bu, soyutlamanın başarısız olduğu bir durumdur. İnsanlar, "Jon" ve "Inbar" ın "eşittir" fiiliyle birleştirilen nesneler olduğunu belirlemek için bağlam ipuçlarını kullanabilir, ancak Python yorumlayıcısı daha gerçek fikirlidir.

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

mantıksal olarak şuna eşdeğerdir:

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

Bob kullanıcısı için şuna eşdeğerdir:

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

orOperatör pozitif ilk argüman seçer gerçeği değeri :

if ("Jon"):

Ve "Jon" pozitif bir doğruluk değerine sahip olduğu için ifblok çalışır. "Erişim verildi" nin verilen addan bağımsız olarak yazdırılmasına neden olan budur.

Bütün bu akıl yürütme, ifade için de geçerlidir if "Kevin" or "Jon" or "Inbar" == name. ilk değer, "Kevin"true olduğundan ifblok yürütülür.


Bu koşulu düzgün bir şekilde inşa etmenin iki yaygın yolu vardır.

  1. ==Her bir değeri açıkça kontrol etmek için birden çok operatör kullanın :
    if name == "Kevin" or name == "Jon" or name == "Inbar":

  2. Bir dizi geçerli değer oluşturun ve inoperatörü üyeliği test etmek için kullanın :
    if name in {"Kevin", "Jon", "Inbar"}:

Genel olarak, okunması daha kolay ve aynı zamanda daha hızlı olduğu için ikincisi tercih edilmelidir:

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

if a == b or c or d or e: ...Gerçekten de bu şekilde ayrıştırılmış bir kanıt isteyenler için . Yerleşik astmodül bir cevap sağlar:

>>> 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=[])])"
>>>

Yani testbir ifböyle deyimi görünüyor:

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

Görüldüğü gibi, boolean operatör var orbirden uygulanan values, yani a == bve c, dve e.


("Kevin", "Jon", "Inbar")Küme yerine demet seçmenin belirli bir nedeni var mı {"Kevin", "Jon", "Inbar"} ?
İnsan

2
Gerçekten değil, çünkü değerlerin tümü hashable ise ikisi de çalışır. Set üyelik testi, tuple üyelik testinden daha büyük bir O karmaşıklığına sahiptir, ancak bir set oluşturmak, bir demet oluşturmaktan biraz daha pahalıdır. Bunun gibi küçük koleksiyonlar için büyük ölçüde bir yıkama olduğunu düşünüyorum. Timeit ile oynamak benim makinemdekinden a in {b, c, d}yaklaşık iki kat daha hızlı a in (b, c, d). Bunun performans açısından kritik bir kod parçası olup olmadığı hakkında düşünülmesi gereken bir şey.
Kevin

3
Bir 'if' yan tümcesinde 'in' kullanırken Tuple veya list? üyelik testi için set değişmezlerini önerir. Gönderimi güncelleyeceğim.
Kevin

Modern Python'da, kümenin bir sabit olduğunu kabul eder ve onu bir frozensetyerine yapar , bu nedenle inşa kümesi ek yükü orada değildir. dis.dis(compile("1 in {1, 2, 3}", '<stdin>', 'eval'))
endolith

1

Basit mühendislik problemi, biraz daha ileri gidelim.

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

Ancak, C dilinden miras alınan Python, sıfır olmayan bir tamsayının mantıksal değerini True olarak değerlendirir.

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

Şimdi, Python bu mantığı temel alıyor ve tamsayılar gibi mantık değişmezlerini kullanmanıza izin veriyor.

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

En sonunda

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

Bunu yazmanın doğru yolu şudur:

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

Güvenlik için, şifreleri sabit kodlamamanızı da öneririm.


1

3 durum kontrolü vardır if name == "Kevin" or "Jon" or "Inbar":

  • name == "Kevin"
  • "Jon"
  • "Barda"

ve bu if deyimi eşdeğerdir

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

Yana elif "Jon"her zaman doğru olacaktır herhangi bir kullanıcı erişim izninin verilmesi, böylece

Çözüm


Aşağıdaki yöntemlerden herhangi birini kullanabilirsiniz

Hızlı

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

Yavaş

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

Yavaş + Gereksiz kod

if name == "Kevin":
    print("Access granted.")
elif name == "Jon":
    print("Access granted.")
elif name == "Inbar":
    print("Access granted.")
else:
    print("Access denied.")
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.