Python'da __eq__ açısından __ne__ uygulamalı mıyım?


101

__eq__Yöntemi geçersiz kılmak istediğim bir sınıfım var . Ben geçersiz gerektiğini mantıklı görünüyor __ne__yanı yöntemini, ancak uygulamaya mantıklıdır __ne__terimlerin içinde __eq__gibi?

class A:

    def __init__(self, attr):
        self.attr = attr

    def __eq__(self, other):
        return self.attr == other.attr
    
    def __ne__(self, other):
        return not self.__eq__(other)

Ya da Python'un bu yöntemleri kullanma biçiminde, bunu iyi bir fikir değil kılan bir şey mi var?

Yanıtlar:


60

Evet, bu gayet iyi. Aslında, dokümantasyon sizden __ne__şunları tanımladığınızda tanımlamanızı ister __eq__:

Karşılaştırma operatörleri arasında zımni ilişkiler yoktur. Gerçeği x==yanlamına gelmez x!=y yanlıştır. Buna göre, tanımlanırken __eq__(), __ne__()operatörlerin beklendiği gibi davranması için de tanımlanmalıdır .

Çoğu durumda (bunun gibi), sonucunu olumsuzlamak kadar basit olacaktır __eq__, ancak her zaman değil.


12
bu doğru cevaptır (burada, @ aaron-hall tarafından). Eğer alıntı dokümantasyon yok değil uygulamak için teşvik __ne__kullanarak __eq__bunu uygulamak sadece o.
guyarad

2
@guyarad: Aslında doğru şekilde yetkilendirilmediği için Aaron'un cevabı hala biraz yanlış; NotImplementedbir taraftan bir dönüşü __ne__diğer taraftan delege etmek için bir işaret olarak ele almak yerine not self == other, (işlenenin __eq__diğer işleneni nasıl karşılaştıracağını bilmediğini varsayarak ) örtük __eq__olarak diğer taraftan delege etmek , sonra onu tersine çevirmektir. Garip türler için, örneğin SQLAlchemy ORM alanları için bu sorunlara neden olur .
ShadowRanger

1
ShadowRanger'ın eleştirisi yalnızca çok patolojik vakalar (IMHO) için geçerli olacaktır ve aşağıdaki cevabımda tam olarak ele alınmıştır.
Aaron Hall

2
Daha yeni belgeler (en azından 3.7 için, daha da erken olabilir) __ne__otomatik olarak delege eder __eq__ve bu yanıttaki alıntı artık belgelerde yoktur. Sonuç olarak, yalnızca uygulamak __eq__ve __ne__delege etmek için mükemmel bir pitoniktir .
bluesummers

135

Python, __ne__()operatörü temel alarak uygulamalı __eq__mıyım?

Kısa Cevap: Gerekirse Do not, kullanım uygulamak, ama ==, değil__eq__

Python 3'te, varsayılan olarak !=olumsuzlamadır ==, bu nedenle a yazmanız bile gerekmez __ne__ve dokümantasyon artık bir tane yazma konusunda fikir sahibi değildir .

Genel olarak konuşursak, yalnızca Python 3 kodlu kod için, örneğin yerleşik bir nesne için üst uygulamayı gölgede bırakmanız gerekmedikçe bir tane yazmayın.

Yani, Raymond Hettinger'in yorumunu aklınızda bulundurun :

__ne__Yöntem otomatik takip __eq__eğer sadece __ne__zaten Bir üst sınıfta tanımlanmış değildir. Yani, bir yerleşikten miras alıyorsanız, her ikisini de geçersiz kılmak en iyisidir.

Python 2'de çalışması için kodunuza ihtiyacınız varsa, Python 2 için öneriyi izleyin ve Python 3'te gayet iyi çalışacaktır.

Python 2'de, Python kendisi otomatik olarak başka açısından herhangi operasyonu uygulamıyor - bu nedenle, tanımlamalıdır __ne__açısından ==yerine __eq__. ÖRNEĞİN

class A(object):
    def __eq__(self, other):
        return self.value == other.value

    def __ne__(self, other):
        return not self == other # NOT `return not self.__eq__(other)`

Kanıtı görün

  • ve __ne__()dayalı operatörü uygulamak__eq__
  • __ne__Python 2'de hiç uygulanmıyor

aşağıdaki gösterimde yanlış davranış sağlar.

Uzun cevap

Dokümantasyon Python 2 için diyor ki:

Karşılaştırma operatörleri arasında zımni ilişkiler yoktur. Gerçeği x==yanlamına gelmez x!=yyanlıştır. Buna göre, tanımlanırken __eq__(), __ne__()operatörlerin beklendiği gibi davranması için de tanımlanmalıdır .

Bu __ne__, tersi olarak tanımlarsak __eq__tutarlı davranışlar elde edebileceğimiz anlamına gelir .

Belgelerin bu bölümü Python 3 için güncellenmiştir :

Varsayılan olarak, __ne__()delege __eq__()olmadıkça sonucu tersine çevirir NotImplemented.

ve "yenilikler" bölümünde , bu davranışın değiştiğini görüyoruz:

  • !=Şimdi tersini döndürür ==sürece, ==getiri NotImplemented.

Uygulama için __ne__,==__eq__ yöntemi doğrudan kullanmak yerine operatörü kullanmayı tercih ederiz, böylece kontrol edilen tür için self.__eq__(other)bir alt sınıf dönerse NotImplemented, Python other.__eq__(self) belgelerden uygun şekilde kontrol eder :

NotImplementednesne

Bu türün tek bir değeri vardır. Bu değere sahip tek bir nesne var. Bu nesneye yerleşik ad aracılığıyla erişilir NotImplemented. Sayısal yöntemler ve zengin karşılaştırma yöntemleri, sağlanan işlenenler için işlemi uygulamazlarsa bu değeri döndürebilir. (Tercüman daha sonra operatöre bağlı olarak yansıtılan işlemi veya başka bir geri dönüşü deneyecektir.) Doğruluk değeri doğrudur.

Onlar, Python çekler aynı tip değilseniz, zengin karşılaştırma operatörü verildiğinde ise otherbir alt tipi olduğunu ve tanımlanmış olduğu operatöre varsa, o kullanır other(için ters ilk 'ın metodunu <, <=, >=ve >). Eğer NotImplementeddöndürülür, daha sonra tam tersi bir yöntem kullanır. (O mu değil iki kez aynı yöntemle kontrol edin.) Kullanarak ==bu mantık gerçekleşmesi için operatör izin verir.


Beklentiler

Anlamsal olarak, __ne__eşitlik denetimi açısından uygulamalısınız çünkü sınıfınızın kullanıcıları aşağıdaki işlevlerin tüm A örnekleri için eşdeğer olmasını bekler:

def negation_of_equals(inst1, inst2):
    """always should return same as not_equals(inst1, inst2)"""
    return not inst1 == inst2

def not_equals(inst1, inst2):
    """always should return same as negation_of_equals(inst1, inst2)"""
    return inst1 != inst2

Yani, yukarıdaki her iki işlev de her zaman aynı sonucu döndürmelidir. Ancak bu, programcıya bağlıdır.

Aşağıdakilere __ne__dayalı olarak tanımlarken beklenmeyen davranışın gösterilmesi __eq__:

İlk kurulum:

class BaseEquatable(object):
    def __init__(self, x):
        self.x = x
    def __eq__(self, other):
        return isinstance(other, BaseEquatable) and self.x == other.x

class ComparableWrong(BaseEquatable):
    def __ne__(self, other):
        return not self.__eq__(other)

class ComparableRight(BaseEquatable):
    def __ne__(self, other):
        return not self == other

class EqMixin(object):
    def __eq__(self, other):
        """override Base __eq__ & bounce to other for __eq__, e.g. 
        if issubclass(type(self), type(other)): # True in this example
        """
        return NotImplemented

class ChildComparableWrong(EqMixin, ComparableWrong):
    """__ne__ the wrong way (__eq__ directly)"""

class ChildComparableRight(EqMixin, ComparableRight):
    """__ne__ the right way (uses ==)"""

class ChildComparablePy3(EqMixin, BaseEquatable):
    """No __ne__, only right in Python 3."""

Eşdeğer olmayan örnekleri örnekleyin:

right1, right2 = ComparableRight(1), ChildComparableRight(2)
wrong1, wrong2 = ComparableWrong(1), ChildComparableWrong(2)
right_py3_1, right_py3_2 = BaseEquatable(1), ChildComparablePy3(2)

Beklenen davranış:

(Not: Aşağıdakilerin her birinin her ikinci iddiası eşdeğerdir ve bu nedenle ondan öncekine mantıksal olarak gereksiz olsa da, biri diğerinin alt sınıfı olduğunda sıranın önemli olmadığını göstermek için onları dahil ediyorum . )

Bu örnekler aşağıdakilerle __ne__uygulanmıştır ==:

assert not right1 == right2
assert not right2 == right1
assert right1 != right2
assert right2 != right1

Python 3 altında test edilen bu örnekler de doğru şekilde çalışır:

assert not right_py3_1 == right_py3_2
assert not right_py3_2 == right_py3_1
assert right_py3_1 != right_py3_2
assert right_py3_2 != right_py3_1

Ve bunların __ne__uygulandığını hatırlayın __eq__- bu beklenen davranış olsa da, uygulama yanlıştır:

assert not wrong1 == wrong2         # These are contradicted by the
assert not wrong2 == wrong1         # below unexpected behavior!

Beklenmedik Davranış:

Bu karşılaştırmanın yukarıdaki karşılaştırmalarla çeliştiğini unutmayın ( not wrong1 == wrong2).

>>> assert wrong1 != wrong2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

ve,

>>> assert wrong2 != wrong1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

__ne__Python 2'de atlamayın

__ne__Python 2'de uygulamayı atlamamanız gerektiğine dair kanıt için şu eşdeğer nesnelere bakın:

>>> right_py3_1, right_py3_1child = BaseEquatable(1), ChildComparablePy3(1)
>>> right_py3_1 != right_py3_1child # as evaluated in Python 2!
True

Yukarıdaki sonuç şöyle olmalıdır False!

Python 3 kaynağı

İçin varsayılan CPython uygulama __ne__olduğu typeobject.ciçindeobject_richcompare :

case Py_NE:
    /* By default, __ne__() delegates to __eq__() and inverts the result,
       unless the latter returns NotImplemented. */
    if (Py_TYPE(self)->tp_richcompare == NULL) {
        res = Py_NotImplemented;
        Py_INCREF(res);
        break;
    }
    res = (*Py_TYPE(self)->tp_richcompare)(self, other, Py_EQ);
    if (res != NULL && res != Py_NotImplemented) {
        int ok = PyObject_IsTrue(res);
        Py_DECREF(res);
        if (ok < 0)
            res = NULL;
        else {
            if (ok)
                res = Py_False;
            else
                res = Py_True;
            Py_INCREF(res);
        }
    }
    break;

Ama varsayılan __ne__kullanır __eq__?

Python 3'ün __ne__C düzeyindeki varsayılan uygulama ayrıntısı __eq__, daha yüksek düzey ==( PyObject_RichCompare ) daha az verimli olacağı için kullanır - ve bu nedenle de işlemesi gerekir NotImplemented.

Doğru __eq__bir şekilde uygulanırsa, olumsuzlama ==da doğrudur - ve bizim düşük seviyeli uygulama ayrıntılarından kaçınmamızı sağlar __ne__.

Kullanımı ==, düşük seviyeli mantığımızı tek bir yerde tutmamızı ve adres vermekten kaçınmamızı sağlar .NotImplemented__ne__

Yanlışlıkla bunun ==geri dönebileceği varsayılabilir NotImplemented.

Aslında, __eq__kimliğini kontrol eden varsayılan uygulamasıyla aynı mantığı kullanır ( do_richcompare ve aşağıdaki kanıtımıza bakın)

class Foo:
    def __ne__(self, other):
        return NotImplemented
    __eq__ = __ne__

f = Foo()
f2 = Foo()

Ve karşılaştırmalar:

>>> f == f
True
>>> f != f
False
>>> f2 == f
False
>>> f2 != f
True

Verim

Benim sözüme güvenmeyin, bakalım neyin daha performanslı olduğunu görelim:

class CLevel:
    "Use default logic programmed in C"

class HighLevelPython:
    def __ne__(self, other):
        return not self == other

class LowLevelPython:
    def __ne__(self, other):
        equal = self.__eq__(other)
        if equal is NotImplemented:
            return NotImplemented
        return not equal

def c_level():
    cl = CLevel()
    return lambda: cl != cl

def high_level_python():
    hlp = HighLevelPython()
    return lambda: hlp != hlp

def low_level_python():
    llp = LowLevelPython()
    return lambda: llp != llp

Sanırım bu performans rakamları kendileri için konuşuyor:

>>> import timeit
>>> min(timeit.repeat(c_level()))
0.09377292497083545
>>> min(timeit.repeat(high_level_python()))
0.2654011140111834
>>> min(timeit.repeat(low_level_python()))
0.3378178110579029

Bu, low_level_pythonPython'da, aksi takdirde C düzeyinde ele alınacak bir mantık yaptığını düşündüğünüzde mantıklıdır .

Bazı eleştirmenlere yanıt

Başka bir cevaplayıcı yazıyor:

Aaron Hall'un uygulaması not self == otherait __ne__yönteme onu asla geri dönemez olarak yanlıştır NotImplemented( not NotImplementededilmektedir False) ve bu nedenle __ne__önceliği vardır yöntem geri düşebilir asla __ne__öncelik yoktur yöntemle.

__ne__Asla geri dönmemiş olmak NotImplementedyanlış yapmaz. Bunun yerine, ile NotImplementedeşitlik kontrolü yoluyla önceliklendirmeyi ele alıyoruz ==. ==Doğru uygulandığını varsayarsak , işimiz bitti.

not self == othereskiden __ne__yöntemin varsayılan Python 3 uygulamasıydı, ancak bir hataydı ve ShadowRanger'ın fark ettiği gibi Ocak 2015'te Python 3.4'te düzeltildi (bkz. sorun # 21408).

Peki, bunu açıklayalım.

Daha önce, Python 3 varsayılan tutamaçlarıyla belirtildiği gibi __ne__eğer ilk kontrol ederek self.__eq__(other)döner NotImplementedile kontrol edilmelidir - (a tek) iseğer öyleyse ve döndü aksi takdirde tersini dönmelidir. İşte sınıf karışımı olarak yazılan mantık:

class CStyle__ne__:
    """Mixin that provides __ne__ functionality equivalent to 
    the builtin functionality
    """
    def __ne__(self, other):
        equal = self.__eq__(other)
        if equal is NotImplemented:
            return NotImplemented
        return not equal

Bu, C seviyesi Python API'sinin doğruluğu için gereklidir ve Python 3'te tanıtılmıştır.

gereksiz. __ne__Kendi kontrollerini uygulayanlar ve __eq__doğrudan veya aracılığıyla delege edenler de dahil olmak üzere ilgili tüm yöntemler kaldırıldı ==ve ==bunu yapmanın en yaygın yolu buydu.

Simetri Önemli mi?

Bizim kalıcı kritik taşıma bulunması için de bir patolojik örnek sağlar NotImplementedolarak __ne__, her şeyden önce simetri değerlemesi. Açık bir örnekle tartışmayı çelik adam edelim:

class B:
    """
    this class has no __eq__ implementation, but asserts 
    any instance is not equal to any other object
    """
    def __ne__(self, other):
        return True

class A:
    "This class asserts instances are equivalent to all other objects"
    def __eq__(self, other):
        return True

>>> A() == B(), B() == A(), A() != B(), B() != A()
(True, True, False, True)

Dolayısıyla, bu mantıkla, simetriyi korumak için __ne__, Python sürümünden bağımsız olarak karmaşık olanı yazmamız gerekir .

class B:
    def __ne__(self, other):
        return True

class A:
    def __eq__(self, other):
        return True
    def __ne__(self, other):
        result = other.__eq__(self)
        if result is NotImplemented:
            return NotImplemented
        return not result

>>> A() == B(), B() == A(), A() != B(), B() != A()
(True, True, True, True)

Görünüşe göre bu örneklerin hem eşit hem de eşit olmadığı konusunda hiçbir fikrimiz yok.

Simetrinin, mantıklı kod varsayımından ve dokümantasyondaki tavsiyeleri takip etmekten daha az önemli olduğunu öneriyorum.

Bununla birlikte, eğer A'nın mantıklı bir uygulaması olsaydı __eq__, o zaman burada hala benim yönümü izleyebilirdik ve yine de simetriye sahip olurduk:

class B:
    def __ne__(self, other):
        return True

class A:
    def __eq__(self, other):
        return False         # <- this boolean changed... 

>>> A() == B(), B() == A(), A() != B(), B() != A()
(False, False, True, True)

Sonuç

Python 2 uyumlu kod ==için uygulamak için kullanın __ne__. Bu daha fazla:

  • doğru
  • basit
  • icracı

Yalnızca Python 3'te, C düzeyindeki düşük düzey olumsuzlamayı kullanın - daha da basit ve performanslıdır (ancak programcı doğru olup olmadığını belirlemekten sorumludur ).

Yine, do not üst düzey Python yazma düşük seviyeli mantık.


3
Mükemmel örnekler! Sürprizin bir kısmı, işlenenlerin sırasının "sağ taraftaki" yansımaları olan bazı sihirli yöntemlerin aksine hiç önemli olmamasıdır . Kaçırdığım (ve bana çok zamana mal olan) bölümü yeniden yinelemek için: Kodun üst sınıfa veya operatörün solunda alt sınıfa sahip olup olmadığına bakılmaksızın, ilk olarak alt sınıfın zengin karşılaştırma yöntemi denenir. Bu nedenle a1 != c2iade False--- o kaçmadın a1.__ne__ama c2.__ne__hangi etkisiz, mixin en __eq__ yöntemini. Yana NotImplementedtruthy olduğunu not NotImplementedolduğunu False.
Kevin J. Chase

2
Son güncellemeleriniz performans avantajını başarılı bir şekilde gösteriyor not (self == other), ancak hiç kimse bunun hızlı olmadığını savunmuyor (zaten Py2'deki diğer seçeneklerden daha hızlı). Sorun, bazı durumlarda yanlış olmasıdır ; Python eskiden yapıyordunot (self == other) , ancak rastgele alt sınıfların varlığında yanlış olduğu için değişti . En hızlıdan yanlış cevaba giden hala yanlıştır .
ShadowRanger

1
Spesifik örnek gerçekten önemsiz. Sorun şu ki, uygulamanızda, __ne__delegelerinizin __eq__(gerekirse her iki tarafın) davranışları , ancak her ikisi de "pes etse " bile asla__ne__ diğer tarafa geri dönmüyor __eq__. Doğru __ne__delegeler kendi başına __eq__ , ancak bu geri gelirse NotImplemented, diğer tarafın __ne__tersini çevirmek yerine diğer tarafa geri döner __eq__(çünkü diğer taraf açık bir şekilde delege etmeyi seçmemiş olabilir __eq__ve yapmamalısınız. onun için bu kararı veriyor).
ShadowRanger

1
@AaronHall: Bunu bugün yeniden incelerken , uygulamanızın normal olarak alt sınıflar için sorunlu olduğunu düşünmüyorum (onu kırmak için aşırı derecede karmaşık olurdu ve ebeveyn hakkında tam bilgi sahibi olduğu varsayılan alt sınıf bundan kaçınabilmelidir. ). Ama cevabımda sadece kıvrımsız bir örnek verdim. Patolojik olmayan durum, SQLAlchemy'nin ORM'sidir, burada ne ya da geri döner ne __eq__de __ne__döner , daha ziyade bir proxy nesnesi ("doğru" olur). Yanlış uygulama , karşılaştırma için siparişin önemli olduğu anlamına gelir (yalnızca bir siparişte bir proxy alırsınız). TrueFalse__ne__
ShadowRanger

1
Açık olmak gerekirse, vakaların% 99'unda (veya belki% 99,999'unda) çözümünüz iyi ve (tabii ki) daha hızlı. Ancak , kodunun başkaları tarafından kullanılabileceği bir kütüphane yazarı olarak, iyi olmadığı durumlar üzerinde kontrolünüz olmadığından Operatörün aşırı yüklenmesi için genel sözleşmeye uymak için doğru uygulamayı kullanın ve karşılaşabileceğiniz diğer kodlarla çalışın. Neyse ki, Py3'te bunların hiçbiri önemli değil, çünkü __ne__tamamen atlayabilirsiniz . Bundan bir yıl sonra Py2 ölecek ve biz bunu görmezden geliyoruz. :-)
ShadowRanger

10

Sadece kayıt için, kanonik olarak doğru ve çapraz bir Py2 / Py3 taşınabilir __ne__şöyle görünür:

import sys

class ...:
    ...
    def __eq__(self, other):
        ...

    if sys.version_info[0] == 2:
        def __ne__(self, other):
            equal = self.__eq__(other)
            return equal if equal is NotImplemented else not equal

Bu __eq__, tanımlayabileceğiniz herhangi biriyle çalışır :

  • Bunun aksine not (self == other), ilgili sınıflardan birinin __ne__sonucunun noton sonucuyla aynı olduğunu ima etmediği karşılaştırmaları içeren bazı sinir bozucu / karmaşık durumlara müdahale etmez __eq__(örneğin, SQLAlchemy'nin ORM'si, her ikisi __eq__ve __ne__özel proxy nesneleri döndürür, değil Trueveya Falseve notsonucuna çalışmak , doğru proxy nesnesi yerine __eq__döndürür False).
  • Aksine not self.__eq__(other), bu doğru delegeler __ne__diğer örneğinin zaman self.__eq__döner NotImplemented( not self.__eq__(other)çünkü ekstra yanlış olur NotImplemented, bu nedenle zaman truthy olan __eq__karşılaştırma yapmak için nasıl bilmiyordum __ne__dönecekti Falseiki nesnenin eşit olduğunu ima zaman aslında sadece sorulan nesnenin hiçbir fikri yoktu, bu da eşit olmayan bir varsayılan anlamına gelir)

İade __eq__kullanmıyorsanız NotImplemented, bu işe yarar (anlamsız ek yük ile), eğer NotImplementedbazen kullanıyorsa, bu onu düzgün bir şekilde ele alır. Ve Python sürüm kontrolü, sınıf importPython 3'te __ne__tanımsız bırakılırsa, Python'un yerel, verimli geri dönüş __ne__uygulamasının (yukarıdakinin C sürümü) devralmasına izin verdiği anlamına gelir .


Bu neden gerekli

Python aşırı yükleme kuralları

Diğer çözümler yerine bunu neden yaptığınızın açıklaması biraz gizemli. Python'un, operatörleri aşırı yükleme ve özellikle karşılaştırma operatörleri hakkında birkaç genel kuralı vardır:

  1. (Tüm operatörler için geçerlidir) Çalışırken LHS OP RHS, deneyin LHS.__op__(RHS)ve geri dönerse NotImplemented, deneyin RHS.__rop__(LHS). İstisna: Eğer RHSbir alt sınıfı olan LHSbireyin sınıfın ardından sınamak RHS.__rop__(LHS) ilk . Karşılaştırma operatörleri durumunda, __eq__ve __ne__kendi "rop" ler vardır (test sırası böylece __ne__ise LHS.__ne__(RHS), o zaman RHS.__ne__(LHS)eğer, tersine RHSbir alt sınıfıdır LHS'ın sınıfında)
  2. "Değiştirilen" operatör fikrinin yanı sıra, operatörler arasında zımni bir ilişki yoktur. Örneğin, aynı sınıf için bile, LHS.__eq__(RHS)döndürme , geri dönüşler Trueanlamına gelmez (aslında, operatörlerin boole değerleri döndürmesi bile gerekmez; SQLAlchemy gibi ORM'ler kasıtlı olarak bunu yapmaz, daha anlamlı bir sorgu sözdizimi sağlar). Python 3 itibariyle, varsayılan uygulama bu şekilde davranır, ancak bu sözleşmeye dayalı değildir; tam tersi olmayan şekillerde geçersiz kılabilirsiniz .LHS.__ne__(RHS)False__ne____ne____eq__

Bu, karşılaştırıcıları aşırı yüklemek için nasıl geçerlidir?

Yani bir operatörü aşırı yüklediğinizde iki işiniz olur:

  1. İşlemi kendiniz nasıl uygulayacağınızı biliyorsanız, bunu sadece karşılaştırmanın nasıl yapılacağına dair kendi bilginizi kullanarak yapın (hiçbir zaman işlemin diğer tarafına açık veya örtük olarak yetki vermeyin; böyle yapmak yanlışlık ve / veya sonsuz tekrarlama riski taşır, nasıl yaptığına bağlı olarak)
  2. Eğer varsa yok operasyonu kendinizi nasıl uygulanacağı biliyor, her zaman dönmek NotImplementedPython diğer terimi en uygulanmasına temsilci, böylece

İle ilgili sorun not self.__eq__(other)

def __ne__(self, other):
    return not self.__eq__(other)

asla diğer tarafa yetki vermez (ve __eq__uygun şekilde dönerse yanlıştır NotImplemented). Ne zaman self.__eq__(other)döner NotImplemented( "truthy" dir), sessizce dönmek False, bu nedenle A() != something_A_knows_nothing_aboutgetiri False, bu kontrol gerekirken eğer something_A_knows_nothing_aboutörnekleri karşılaştırmak bildiğini Ave bunları yapmazsa, iade olmalıydı Trueiki tarafın nasıl biliyorsa beri ( diğeriyle karşılaştırıldığında, birbirlerine eşit olmadıkları kabul edilir). Eğer A.__eq__yanlış kullanılması (dönen Falseyerine NotImplemented, o zaman bundan "doğru" olduğunu, diğer tarafı tanımadığı zaman) Abireyin perspektifinden, dönen True(çünkü Ao eşit değil yani, eşit olduğunu düşünmüyor), ancak bu olabilir yanlışsomething_A_knows_nothing_abouthiç sorulmadığı için bakış açısı something_A_knows_nothing_about; A() != something_A_knows_nothing_aboutbiter True, ancak something_A_knows_nothing_about != A()olabilir Falseveya başka herhangi bir dönüş değeri.

İle ilgili sorun not self == other

def __ne__(self, other):
    return not self == other

daha inceliklidir. __ne__Mantıksal olarak tersi olan tüm sınıflar dahil, sınıfların% 99'u için doğru olacaktır __eq__. Ancak not self == otheryukarıda bahsedilen kuralların her ikisini de bozar, yani mantıksal olarak tersi __ne__ olmayan sınıflar __eq__için sonuçlar bir kez daha simetrik değildir, çünkü işlenenlerden biri __ne__, diğeri olsa bile , hiçbir zaman uygulayıp uygulamayacağı sorulmaz. operand yapamaz. En basit örnek, tüm karşılaştırmalar Falseiçin dönen ve her ikisi de dönen bir garip sınıftır . Doğru bir uygulamayla ( karşılaştırmanın nasıl yapılacağını bilmediğinde geri dönen ), ilişki simetriktir; veA() == Incomparable()A() != Incomparable()FalseA.__ne__NotImplementedA() != Incomparable()Incomparable() != A()sonuç üzerinde anlaşın (çünkü ilk durumda, A.__ne__geri döner NotImplemented, sonra Incomparable.__ne__geri döner False, ikincisinde doğrudan Incomparable.__ne__geri döner False). Ama ne zaman A.__ne__olarak uygulanır return not self == other, A() != Incomparable()getiriler True(çünkü A.__eq__döner, değil NotImplemented, daha sonra Incomparable.__eq__döner Falseve A.__ne__tersine döndüğü o kadar True), ise Incomparable() != A()getirilerFalse.

Burada bunun uygulamalı bir örneğini görebilirsiniz .

Açıkçası, her Falseikisi için de geri dönen __eq__ve __ne__biraz tuhaf olan bir sınıf . Ama daha önce belirtildiği gibi __eq__ve __ne__hatta dönmek gerekmez True/ False; SQLAlchemy ORM Karşılaştırıcıların ile sınıfları sahip olduğunu döndürür sorgu inşa edilmesini öngören özel bir proxy nesne değil, True/ Falsetüm (onlar ediyoruz "truthy" eğer bir boolean bağlamda değerlendirildiğinde, ancak böyle bir bağlamda değerlendirilmelidir gerekiyordu asla konum) de.

Aşırı başarısız olarak __ne__düzgün, sen olacaktır kod olarak, bu tür sınıfları kırmak:

 results = session.query(MyTable).filter(MyTable.fieldname != MyClassWithBadNE())

Çalışacak (SQLAlchemy'nin MyClassWithBadNEbir SQL dizesine nasıl ekleneceğini bildiğini varsayarsak ; bu, tür bağdaştırıcıları ile hiçbir MyClassWithBadNEşekilde işbirliği yapmak zorunda kalmadan yapılabilir ), beklenen proxy nesnesini şu şekilde filteriletirken:

 results = session.query(MyTable).filter(MyClassWithBadNE() != MyTable.fieldname)

geçen sona erecek filterbir sade False, çünkü self == othergetiri vekil nesne ve not self == othersadece için truthy vekil nesnesini dönüştürür False. Umarım, filtergibi geçersiz argümanların ele alınmasına bir istisna atar False. Ben iddia emin birçok olduğum sürece MyTable.fieldname gereken karşılaştırmanın sol tarafında sürekli olması, genel durumda bu zorlamak için programlı bir neden ve doğru bir jenerik gerçektir kalıntıları olduğu __ne__irade işi iki şekilde de iken,return not self == other sadece eserleri tek bir düzenlemede.


1
Tek doğru, eksiksiz ve dürüst (özür dilerim @AaronHall) cevap. Kabul edilen cevap bu olmalıdır.
Maggyero

IncomparableBu sınıfın ve operatörleri arasındaki tamamlayıcı ilişkiyi kırdığından ve bu nedenle @ AaronHall'ın dediği gibi geçersiz veya "patolojik" bir örnek olarak değerlendirilebileceğinden , sınıfınızdan daha güçlü bir argüman olduğunu düşündüğüm güncel cevabım ilginizi çekebilir . Ve @AaronHall'ın, SQLAlchemy argümanınızın Boolean olmayan bağlamda olduğu için alakasız olarak değerlendirilebileceğini belirttiğinde bir noktaya sahip olduğunu kabul ediyorum. ( !===
İddialarınız

4

Doğru __ne__ uygulama

@ ShadowRanger'ın özel yöntem uygulaması __ne__doğru olanıdır:

def __ne__(self, other):
    result = self.__eq__(other)
    if result is not NotImplemented:
        return not result
    return NotImplemented

Ayrıca , Python belgelerinde belirtildiği gibi, __ne__ Python 3.4'ten beri özel yöntemin varsayılan uygulamasıdır :

Varsayılan olarak, __ne__()delege __eq__()olmadıkça sonucu tersine çevirir NotImplemented.

Ayrıca NotImplementeddesteklenmeyen işlenenler için değer döndürmenin özel yönteme özgü olmadığını unutmayın __ne__. Aslında, tüm özel karşılaştırma yöntemleri 1 ve özel sayısal yöntemler 2NotImplemented , Python belgelerinde belirtildiği gibi desteklenmeyen işlenenler için değer döndürmelidir :

Uygulanmadı

Bu türün tek bir değeri vardır. Bu değere sahip tek bir nesne var. Bu nesneye yerleşik ad aracılığıyla erişilir NotImplemented. Sayısal yöntemler ve zengin karşılaştırma yöntemleri, sağlanan işlenenler için işlemi uygulamazlarsa bu değeri döndürmelidir. (Tercüman daha sonra operatöre bağlı olarak yansıtılan işlemi veya başka bir geri dönüşü deneyecektir.) Doğruluk değeri doğrudur.

Özel sayısal yöntemlere bir örnek Python belgelerinde verilmiştir :

class MyIntegral(Integral):

    def __add__(self, other):
        if isinstance(other, MyIntegral):
            return do_my_adding_stuff(self, other)
        elif isinstance(other, OtherTypeIKnowAbout):
            return do_my_other_adding_stuff(self, other)
        else:
            return NotImplemented

    def __radd__(self, other):
        if isinstance(other, MyIntegral):
            return do_my_adding_stuff(other, self)
        elif isinstance(other, OtherTypeIKnowAbout):
            return do_my_other_adding_stuff(other, self)
        elif isinstance(other, Integral):
            return int(other) + int(self)
        elif isinstance(other, Real):
            return float(other) + float(self)
        elif isinstance(other, Complex):
            return complex(other) + complex(self)
        else:
            return NotImplemented

1 özel karşılaştırma yöntemleri: __lt__, __le__, __eq__, __ne__, __gt__ve __ge__.

2 özel bir sayısal yöntem: __add__, __sub__, __mul__, __matmul__, __truediv__, __floordiv__, __mod__, __divmod__, __pow__, __lshift__, __rshift__, __and__, __xor__, __or__ve __r*__yansıyan ve __i*__yerinde muadilleri.

Yanlış __ne__uygulama # 1

@ Falmarri'nin özel yöntemi __ne__uygulaması yanlış:

def __ne__(self, other):
    return not self.__eq__(other)

Bu uygulamayla ilgili sorun özel yöntemle geri düşebilir olmamasıdır __ne__o değerini döndürür asla gibi diğer işlenenin NotImplemented(ifade not self.__eq__(other)değerine değerlendirir Trueveya Falsezaman onun alt ifade dahil self.__eq__(other)değere değerlendirir NotImplementedifadesi beri bool(NotImplemented)değere değerlendirir True). Değerin Boole değerlendirilmesi NotImplementedkırar tamamlayıcı karşılaştırma operatörleri arasındaki ilişkiyi !=ve ==:

class Correct:

    def __ne__(self, other):
        result = self.__eq__(other)
        if result is not NotImplemented:
            return not result
        return NotImplemented


class Incorrect:

    def __ne__(self, other):
        return not self.__eq__(other)


x, y = Correct(), Correct()
assert (x != y) is not (x == y)

x, y = Incorrect(), Incorrect()
assert (x != y) is not (x == y)  # AssertionError

Yanlış __ne__uygulama # 2

@ AaronHall'ın özel yöntem __ne__uygulaması da yanlış:

def __ne__(self, other):
    return not self == other

Bu gerçeklemenin problemi __eq__, diğer işlenenin özel yöntemini atlayarak doğrudan diğer işlenenin özel yöntemine __ne__geri dönmesidir, çünkü değeri asla döndürmez NotImplemented(ifade diğer işlenenin not self == otherözel yöntemine geri döner __eq__ve şu şekilde değerlendirilir) değeri Trueveya False). Bir yöntemi atlamak yanlıştır çünkü bu yöntemin nesnenin durumunu güncellemek gibi yan etkileri olabilir :

class Correct:

    def __init__(self):
        self.counter = 0

    def __ne__(self, other):
        self.counter += 1
        result = self.__eq__(other)
        if result is not NotImplemented:
            return not result
        return NotImplemented


class Incorrect:

    def __init__(self):
        self.counter = 0

    def __ne__(self, other):
        self.counter += 1
        return not self == other


x, y = Correct(), Correct()
assert x != y
assert x.counter == y.counter

x, y = Incorrect(), Incorrect()
assert x != y
assert x.counter == y.counter  # AssertionError

Karşılaştırma işlemlerini anlama

Matematikte, bir X kümesi üzerindeki R ikili ilişkisi , X 2'deki sıralı çiftler kümesidir ( xy )  . R'deki ( xy )  ifadesi " x , R ile y ile ilişkilidir " okur ve xRy ile gösterilir .

Bir X kümesi üzerindeki R ikili ilişkisinin özellikleri :

  • R, bir dönüşlü zaman tüm x de , X , XRX .
  • R, bir irreflexive (aynı zamanda katı ) zaman tüm x de X değil, xRx .
  • R, bir simetrik olduğunda tüm x ve y de , X ise, xRy sonra yRx .
  • R, bir antisymmetric zaman tüm x ve y de , X ise, xRy ve yRx sonra X  =  Y .
  • R, bir geçişli olduğunda tüm x , y ve z içinde X ise, xRy ve yRz sonra xRz .
  • R, bir connex (aynı zamanda toplam ) zaman tüm x ve y de x , xRy veya yRx .
  • R, bir bir eşdeğerlik ilişkisi olduğunda R, dönüşlü, simetrik ve geçişlidir.
    Örneğin, =. Ancak ≠ yalnızca simetriktir.
  • R, bir bir sipariş ilişkisi olduğunda R, dönüşlü, antisymmetric ve geçişlidir.
    Örneğin, ≤ ve ≥.
  • R, a, sırasız ilişkisi olduğunda R, irreflexive, antisymmetric ve geçişlidir.
    Örneğin, <ve>. Ancak ≠ yalnızca yansıtma yapmaz.

Bir X kümesi üzerindeki iki ikili ilişki R ve S üzerinde işlemler :

  • Bunun tersi bir R ikili ilişkidir R, T  = {( yx |) xRy üzerinde} X .
  • Tamamlayıcı bir R ikili ilişki alışılagelen R  = {( xy |) değil xRy } X üzerinden .
  • Birliği içinde R ve S ikili ilişkidir R  ∪  S  = {( xy ) | xRy veya xSy üzerinde} X .

Her zaman geçerli olan karşılaştırma ilişkileri arasındaki ilişkiler:

  • 2 tamamlayıcı ilişki: = ve ≠ birbirlerinin tamamlayıcısıdır;
  • 6 ters ilişki: = kendisinin tersidir, ≠ kendisinin tersidir, <ve> birbirlerinin tersidir ve ≤ ve ≥ birbirlerinin tersidir;
  • 2 birleşim ilişkisi: ≤ <ve = birleşimidir ve ≥> ve = birleşimidir.

Yalnızca bağlantı siparişleri için geçerli olan karşılaştırma ilişkileri arasındaki ilişkiler :

  • 4 tamamlayıcı ilişki: <ve ≥ birbirinin tamamlayıcısıdır ve> ve ≤ birbirinin tamamlayıcısıdır.

Yani doğru Python uygulamak için karşılaştırma operatörleri ==, !=, <, >, <=, ve>= karşılaştırma ilişkilerine tekabül =, ≠, <,>, ≤ ve ≥ tüm matematiksel özellikleri ve ilişkileri düzenlemeliyiz yukarıda.

Bir karşılaştırma işlemi , işlenenlerinden birinin sınıfının x operator yözel karşılaştırma yöntemini çağırır __operator__:

class X:

    def __operator__(self, other):
        # implementation

Yana R, bir dönüşlü ima xRx , dönüşlü bir karşılaştırma işlemi x operator y( x == y, x <= yve x >= y) ya da dönüşlü özel karşılaştırma yöntemi çağrısı x.__operator__(y)( x.__eq__(y), x.__le__(y)ve x.__ge__(y)değere değerlendirmelidir) Trueise xve yeğer ifade aynıdır, x is ydeğerlendirir için True. Yana R olduğu irreflexive gerektirir, XRX , bir irreflexive karşılaştırma işlemi x operator y( x != y, x < yve x > y) ya da irreflexive özel karşılaştırma yöntemi çağrısı x.__operator__(y)( x.__ne__(y), x.__lt__(y)ve x.__gt__(y)) değerine değerlendirmelidirFalseEğer aynıdır, bu ifade, eğer olduğu sonucunu . Dönüşlü özelliği karşılaştırma operatör Python'la tarafından kabul edilir ve özel karşılaştırma yöntemi ilişkili , ancak şaşırtıcı bir şekilde kabul karşılaştırması operatörler için ve ve ilgili özel karşılaştırma yöntemleri ve ve irreflexive özelliği karşılaştırma operatör Python'la tarafından kabul edilir ve özel karşılaştırma yöntemi ilişkili , ancak şaşırtıcı olarak kabul karşılaştırması operatörler için ve ve ilgili özel karşılaştırma yöntemleri vex veyx is yTrue==__eq__<=>=__le____ge__!=__ne__<>__lt____gt__. Göz ardı edilen karşılaştırma operatörleri , Python belgelerinde açıklandığı gibi, TypeErrorbunun yerine istisnayı yükseltir (ve bunun yerine ilgili özel karşılaştırma yöntemleri değeri döndürür NotImplemented) :

Eşitlik karşılaştırması ( ==ve !=) için varsayılan davranış , nesnelerin kimliğine bağlıdır. Dolayısıyla, aynı kimliğe sahip örneklerin eşitlik karşılaştırması eşitlikle sonuçlanır ve farklı kimliklere sahip örneklerin eşitlik karşılaştırması eşitsizlikle sonuçlanır. Bu varsayılan davranış için bir motivasyon, tüm nesnelerin dönüşlü olması (yani x is yima etmesi) arzusudur x == y.

Varsayılan bir sipariş karşılaştırması ( <, >, <=ve >=temin edilmemiştir); bir girişim yükseltir TypeError. Bu varsayılan davranış için bir motivasyon, eşitlik için olduğu gibi benzer bir değişmezin olmamasıdır. [Bu yana yanlıştır <=ve >=benzeri yansımalı ==ve <ve >benzeri irreflexive vardır !=.]

Sınıf object, Python belgelerinde açıklandığı gibi, tüm alt sınıfları tarafından miras alınan özel karşılaştırma yöntemlerinin varsayılan uygulamalarını sağlar :

object.__lt__(self, other)
object.__le__(self, other)
object.__eq__(self, other)
object.__ne__(self, other)
object.__gt__(self, other)
object.__ge__(self, other)

Bunlar sözde “zengin karşılaştırma” yöntemleridir. Operatör sembolleri ve yöntem isimleri arasındaki yazışma şu şekildedir: x<yaramalar x.__lt__(y), x<=yaramalar x.__le__(y), x==yaramalar x.__eq__(y), x!=yaramalar x.__ne__(y), x>yaramalar x.__gt__(y)ve x>=y aramalar x.__ge__(y).

Zengin bir karşılaştırma yöntemi, NotImplementedbelirli bir bağımsız değişken çifti için işlemi uygulamazsa tekli döndürebilir .

[…]

Bu yöntemlerin değiştirilmiş bağımsız değişken sürümleri yoktur (sol bağımsız değişken işlemi desteklemediğinde ancak sağ bağımsız değişken desteklediğinde kullanılacaktır); daha doğrusu, __lt__()ve __gt__()birbirlerinin yansımasıdır, __le__()ve __ge__()birbirlerinin yansımasıdır ve __eq__()ve __ne__()kendi yansımasıdır. İşlenenler farklı türdeyse ve sağ işlenen türü, sol işlenen türünün doğrudan veya dolaylı bir alt sınıfı ise, sağ işlenenin yansıtılan yöntemi önceliğe sahiptir, aksi takdirde sol işlenen yöntemi önceliğe sahiptir. Sanal alt sınıflandırma dikkate alınmaz.

Yana R = ( R, T ) T , bir karşılaştırma xRy eşdeğerdir tersi karşılaştırma yR T x (Python belgelerinde gayri adlandırılan "yansıyan"). Öyleyse, bir karşılaştırma işleminin sonucunu hesaplamanın iki yolu vardır x operator y: x.__operator__(y)veya birini çağırmak y.__operatorT__(x). Python aşağıdaki bilgi işlem stratejisini kullanır:

  1. x.__operator__(y)Sağ işlenenin sınıfı, sol işlenen sınıfının bir nesli olmadığı sürece çağırır , bu durumda çağırır y.__operatorT__(x)( sınıfların, atalarının özel karşılaştırma yöntemini geçersiz kılmasına izin verir ).
  2. İşlenenler xve ydesteklenmiyorsa (dönüş değeri ile gösterilir NotImplemented), özel karşılaştırma yöntemini 1. geri dönüş olarak çağırır .
  3. İşlenen halinde xve y(dönüş değeri ile gösterilen desteklenmeyen NotImplemented), bu durum oluşturur TypeErrorkarşılaştırma operatörleri hariç ==ve !=bunun için sırasıyla işlenen kimlik olmayan kimlik test xve ybir şekilde 2 geri dönüş (arasında dönüşlülük özelliği yararlanarak ==ve yansıtma özelliği !=).
  4. Sonucu döndürür.

CPython'da bu, Python koduna çevrilebilen C kodunda uygulanır ( eqfor ==, nefor !=, ltfor <, gtfor >, lefor <=ve gefor adlarıyla >=):

def eq(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__eq__(left)
        if result is NotImplemented:
            result = left.__eq__(right)
    else:
        result = left.__eq__(right)
        if result is NotImplemented:
            result = right.__eq__(left)
    if result is NotImplemented:
        result = left is right
    return result
def ne(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__ne__(left)
        if result is NotImplemented:
            result = left.__ne__(right)
    else:
        result = left.__ne__(right)
        if result is NotImplemented:
            result = right.__ne__(left)
    if result is NotImplemented:
        result = left is not right
    return result
def lt(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__gt__(left)
        if result is NotImplemented:
            result = left.__lt__(right)
    else:
        result = left.__lt__(right)
        if result is NotImplemented:
            result = right.__gt__(left)
    if result is NotImplemented:
        raise TypeError(
            f"'<' not supported between instances of '{type(left).__name__}' "
            f"and '{type(right).__name__}'"
        )
    return result
def gt(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__lt__(left)
        if result is NotImplemented:
            result = left.__gt__(right)
    else:
        result = left.__gt__(right)
        if result is NotImplemented:
            result = right.__lt__(left)
    if result is NotImplemented:
        raise TypeError(
            f"'>' not supported between instances of '{type(left).__name__}' "
            f"and '{type(right).__name__}'"
        )
    return result
def le(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__ge__(left)
        if result is NotImplemented:
            result = left.__le__(right)
    else:
        result = left.__le__(right)
        if result is NotImplemented:
            result = right.__ge__(left)
    if result is NotImplemented:
        raise TypeError(
            f"'<=' not supported between instances of '{type(left).__name__}' "
            f"and '{type(right).__name__}'"
        )
    return result
def ge(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__le__(left)
        if result is NotImplemented:
            result = left.__ge__(right)
    else:
        result = left.__ge__(right)
        if result is NotImplemented:
            result = right.__le__(left)
    if result is NotImplemented:
        raise TypeError(
            f"'>=' not supported between instances of '{type(left).__name__}' "
            f"and '{type(right).__name__}'"
        )
    return result

Yana R = ¬ (¬ R ), bir karşılaştırma xRy eşdeğerdir tamamlayıcı karşılaştırma ¬ ( x ¬ Ry ). ≠ = 'nin tamamlayıcısıdır, bu nedenle özel yöntem varsayılan olarak desteklenen işlenenler için __ne__özel yöntem açısından uygulanırken __eq__, diğer özel karşılaştırma yöntemleri varsayılan olarak bağımsız olarak uygulanır (≤'nin <ve = birleşimidir ve ≥> ve = birleşimidir, şaşırtıcı bir şekilde dikkate alınmaz , bu şu anda özel yöntemler olduğu __le__ve Python belgelerinde__ge__ açıklandığı gibi kullanıcı tarafından uygulanması gerektiği anlamına gelir :

Varsayılan olarak, __ne__()delege __eq__()olmadıkça sonucu tersine çevirir NotImplemented. Karşılaştırma operatörleri arasında başka zımni ilişkiler yoktur, örneğin, gerçeği (x<y or x==y)ima etmez x<=y.

CPython'da bu, Python koduna çevrilebilen C kodunda uygulanır :

def __eq__(self, other):
    return self is other or NotImplemented
def __ne__(self, other):
    result = self.__eq__(other)
    if result is not NotImplemented:
        return not result
    return NotImplemented
def __lt__(self, other):
    return NotImplemented
def __gt__(self, other):
    return NotImplemented
def __le__(self, other):
    return NotImplemented
def __ge__(self, other):
    return NotImplemented

Yani varsayılan olarak:

  • bir karşılaştırma işlemi , karşılaştırma operatörleri dışında x operator yistisnayı ortaya çıkarır ve bunun için sırasıyla işlenenlerin kimliğini ve kimliğini döndürür ve ;TypeError==!=xy
  • özel bir karşılaştırma yöntemi çağrısı , özel karşılaştırma yöntemleri haricindeki x.__operator__(y)değeri döndürür ve bunun için sırasıyla ve işlenenler ve sırasıyla aynı ve özdeş değilse ve değer ise aksi takdirde döndürür.NotImplemented__eq____ne__TrueFalsexyNotImplemented

Son örneğinize: "Bu uygulama __ne__, __eq__yöntem NotImplemented döndürdüğünde yöntemin varsayılan uygulamasının davranışını çoğaltmada başarısız olduğu için , bu yanlıştır." - Akoşulsuz eşitliği tanımlar. Böylece A() == B(). Böylece A() != B() Yanlış olmalı ve bu olduğunu . Verilen örnekler patolojiktir (yani __ne__bir dizge döndürmemelidir ve __eq__buna bağlı olmamalıdır __ne__- daha çok Python 3'teki varsayılan beklentidir __ne__buna bağlı olmalıdır __eq__). Fikrimi değiştirene kadar bu cevapta hala -1'im.
Aaron Hall

@AaronHall Python dil referansından : "Zengin bir karşılaştırma yöntemi, NotImplementedbelirli bir bağımsız değişken çifti için işlemi gerçekleştirmezse singleton'u döndürebilir. Kural olarak Falseve Truebaşarılı bir karşılaştırma için döndürülür. Ancak, bu yöntemler herhangi bir değeri döndürebilir Bu nedenle, karşılaştırma operatörü bir Boole bağlamında kullanılıyorsa (örneğin, bir if ifadesi durumunda), Python bool(), sonucun doğru mu yanlış mı olduğunu belirlemek için değeri çağıracaktır . "
Maggyero

Nihai örnek iki sınıfı vardır, Bbu döner için tüm kontrolleri üzerinde bir truthy dize __ne__ve Ao döner Trueiçin tüm çeklerde __eq__. Bu patolojik bir çelişkidir. Böyle bir çelişki altında, bir istisna yapmak en iyisidir. Bilgisi olmadan B, Asaygı zorunluluğu yoktur B'in uygulanmasını __ne__simetri amaçlar için kullanılabilir. Örnekte bu noktada, Auygulamaların nasıl __ne__olduğu benim için önemsizdir. Lütfen fikrinizi belirtmek için pratik, patolojik olmayan bir vaka bulun. Cevabımı size hitap edecek şekilde güncelledim.
Aaron Hall

SQLAlchemy'nin kullanım durumu, Etki Alanına Özgü Dil içindir. Böyle bir DSL tasarlayan biri, buradaki tüm tavsiyeleri pencereden dışarı atabilir. Bu zayıf benzetmeye işkence etmeye devam etmek için, örneğiniz bir uçağın yarıda geriye doğru uçmasını bekliyor ve benimki yalnızca ileriye doğru uçmasını bekliyor ve bence bu makul bir tasarım kararı. Bence dile getirdiğiniz endişenin yersiz ve geriye dönük.
Aaron Hall

-1

Herşeyden Eğer __eq__, __ne__, __lt__, __ge__, __le__, ve __gt__sınıf için mantıklı, o zaman sadece uygulamak __cmp__yerine. Aksi takdirde, Daniel DiPaolo'nun söylediği parça yüzünden yaptığınız gibi yapın (ben bakmak yerine test ederken;))


12
__cmp__()Özel yöntem artık zengin karşılaştırma operatörleri kullanarak alışması gerektiğini böylece Python 3.x desteklenir.
Don O'Donnell

8
Veya alternatif olarak Python 2.7 veya 3.x kullanıyorsanız, functools.total_ordering dekoratörü de oldukça kullanışlıdır.
Adam Parkin

Söylediğin için teşekkürler. Yine de geçen bir buçuk yılda bu doğrultuda birçok şeyi fark etmeye başladım. ;)
Karl Knechtel
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.