Neden ʻ if None .__ eq __ ("a") "True olarak değerlendiriliyor (ama tam olarak değil)?


146

Python 3.7'de aşağıdaki ifadeyi çalıştırırsanız, (benim testime göre) yazdırılacaktır b:

if None.__eq__("a"):
    print("b")

Ancak, olarak None.__eq__("a")değerlendirilir NotImplemented.

Doğal olarak, "a".__eq__("a")değerlendirir Trueve "b".__eq__("a")değerlendirir False.

Bunu başlangıçta bir işlevin dönüş değerini test ederken keşfettim, ancak ikinci durumda hiçbir şey döndürmedim - yani işlev döndü None.

Burada neler oluyor?

Yanıtlar:


178

Bu, __dunder__eşdeğer operatörleri için çoğu zaman uygun ikame olmadıklarından , yöntemlerin doğrudan kullanılmaması gerektiğine dair harika bir örnektir ; ==Bunun yerine operatörü eşitlik karşılaştırmaları için kullanmalısınız veya bu özel durumda, kontrol ederken Nonekullanın is(daha fazla bilgi için yanıtın altına atlayın).

Yaptığın

None.__eq__('a')
# NotImplemented

Karşılaştırılan NotImplementedtürler farklı olduğu için hangi geri dönüşler . Farklı türde iki nesne gibi, bu şekilde karşılaştırılan bir örneği ele alalım 1ve 'a'. Yapmak (1).__eq__('a')da doğru değil ve geri dönecek NotImplemented. Eşitlik için bu iki değeri karşılaştırmanın doğru yolu,

1 == 'a'
# False

Burada ne olur

  1. İlk önce (1).__eq__('a')denenir, hangi geri döner NotImplemented. Bu, işlemin desteklenmediğini gösterir.
  2. 'a'.__eq__(1)çağrılır, bu da aynı şeyi döndürür NotImplemented. Yani,
  3. Nesneler aynı değilmiş gibi ele alınır ve Falseiade edilir.

İşte bunun nasıl gerçekleştiğini göstermek için bazı özel sınıfları kullanan küçük bir MCVE:

class A:
    def __eq__(self, other):
        print('A.__eq__')
        return NotImplemented

class B:
    def __eq__(self, other):
        print('B.__eq__')
        return NotImplemented

class C:
    def __eq__(self, other):
        print('C.__eq__')
        return True

a = A()
b = B()
c = C()

print(a == b)
# A.__eq__
# B.__eq__
# False

print(a == c)
# A.__eq__
# C.__eq__
# True

print(c == a)
# C.__eq__
# True

Elbette bu , operasyonun neden doğru döndüğünü açıklamıyor . Bunun nedeni NotImplemented, aslında doğru bir değer olmasıdır:

bool(None.__eq__("a"))
# True

İle aynı,

bool(NotImplemented)
# True

Değerleri truthy kabul edilir ve falsy ne hakkında daha fazla bilgi için, dokümanlar bölümüne bakın Hakikat Değer Testi , hem de bu cevabı . Burada bunun NotImplementeddoğru olduğunu belirtmekte fayda var , ancak sınıf geri dönen bir __bool__veya __len__yöntemi Falseveya 0sırasıyla tanımlamış olsaydı farklı bir hikaye olurdu .


==Operatörün işlevsel eşdeğerini istiyorsanız , şunu kullanın operator.eq:

import operator
operator.eq(1, 'a')
# False

Ancak, daha önce de belirtildiği gibi, kontrol ettiğiniz bu özel senaryo için şunu Nonekullanın is:

var = 'a'
var is None
# False

var2 = None
var2 is None
# True

Bunun işlevsel eşdeğeri kullanmaktır operator.is_:

operator.is_(var2, None)
# True

Noneözel bir nesnedir ve herhangi bir zamanda bellekte yalnızca 1 sürüm bulunur. IOW, NoneTypesınıfın yegane singletonudur (ancak aynı nesnenin herhangi bir sayıda referansı olabilir). PEP8 kurallar bu açık hale getirmektedir:

Tekillerle karşılaştırmalar Noneher zaman eşitlik operatörleriyle isveya is notasla eşitlik operatörleriyle yapılmalıdır .

Özetle, singletonların gibi için None, bir referans kontrolü isdaha uygundur, hem olsa ==ve issadece iyi çalışacaktır.


33

Gördüğünüz sonucun nedeni,

None.__eq__("a") # evaluates to NotImplemented

değerlendirilir NotImplementedve NotImplementeddoğruluk değeri şu şekilde belgelenir True:

https://docs.python.org/3/library/constants.html

(Örneğin, ikili özel yöntemlerle döndürülmelidir özel bir değer __eq__(), __lt__(), __add__(), __rsub__(), vs.) işlem diğer tip göre uygulanmadı belirtmek için; Yerinde ikili özel yöntemlerle (örneğin tarafından döndürülebilir __imul__(), __iand__()aynı amaçla, vs.). Doğruluk değeri doğrudur.

__eq()__Yöntemi sadece kullanmak yerine manuel olarak çağırırsanız, ==geri dönme olasılığı NotImplementedve doğruluk değerinin doğru olmasıyla başa çıkmaya hazır olmanız gerekir .


16

Zaten düşündüğünüz gibi None.__eq__("a"), NotImplementedancak şöyle bir şey denerseniz değerlendirir

if NotImplemented:
    print("Yes")
else:
    print("No")

sonuç

Evet

bu, gerçek değerinin NotImplemented true

Bu nedenle sorunun sonucu açıktır:

None.__eq__(something) verim NotImplemented

Ve bool(NotImplemented)True olarak değerlendirilir

Yani if None.__eq__("a")her zaman doğrudur


1

Neden?

A döndürür NotImplemented, evet:

>>> None.__eq__('a')
NotImplemented
>>> 

Ama şuna bakarsanız:

>>> bool(NotImplemented)
True
>>> 

NotImplementedaslında doğru bir değerdir, bu yüzden geri döner b, Truegeçecek her şey, olmayan her şey False.

Nasıl çözeceksin?

Olup olmadığını kontrol etmelisiniz True, bu yüzden gördüğünüz gibi daha şüpheci olun:

>>> NotImplemented == True
False
>>> 

Yani yaparsın:

>>> if None.__eq__('a') == True:
    print('b')


>>> 

Ve gördüğünüz gibi, hiçbir şey geri dönmeyecek.


1
görsel olarak en net cevap - v değerli katkı - teşekkür ederim
scharfmn

1
:) "değerli ekleme" demeye çalıştığım şeyi tam olarak yakalayamıyor (gördüğünüz gibi) - belki de "gecikmiş mükemmellik" istediğim şeydir - şerefe
scharfmn

@scharfmn evet? Bu cevabın daha önce ele alınmamış olanı eklediğini düşündüğünüzü merak ediyorum.
cs95

bir şekilde buradaki görsel / repl şeyler netlik
katıyor

@scharfmn ... İstemler kaldırılmış olsa da kabul edilen cevap da var. Yalnızca terminal istemleri tembelce bırakıldığı için mi oy verdiniz?
cs95
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.