2012-03-23 19 views
14

Masz klasę Pythona, która wymaga testu równości. Python powinien używać typowania kaczego, ale czy jest (lepiej/dokładniej) włączyć lub wykluczyć test isinstance w funkcji eq? Na przykład:Jeśli piszesz kaczkę w Pythonie, czy powinieneś przetestować isinstance?

class Trout(object): 
    def __init__(self, value): 
     self.value = value 

    def __eq__(self, other): 
     return isinstance(other, Trout) and self.value == other.value 
+1

Myślę, że jest to uzasadniony przypadek użycia dla 'isinstance'. –

Odpowiedz

11

Stosowanie isinstance w __eq__ jest dość powszechne. Powodem tego jest to, że jeśli metoda __eq__ ulegnie awarii, może ona zastąpić metodę __eq__ z innego obiektu. Większość normalnych metod jest wywoływana jawnie, ale __eq__ jest nazywana niejawnie, więc wymaga częściej niż wcześniej.

EDIT (dzięki za przypomnienie, Sven Marnach):

Żeby było awaryjne, można zwrócić NotImplemented Singleton, jak w poniższym przykładzie:

class Trout(object): 
    def __init__(self, value): 
     self.value = value 

    def __eq__(self, other): 
     if isinstance(other, Trout): 
      return self.value == other.value 
     else: 
      return NotImplemented 

Załóżmy, że RainbowTrout umie porównać się do Trout lub do innego RainbowTrout, ale tylko Trout tylko wie, jak porównać się z Trout. W tym przykładzie, jeśli przetestujesz mytrout == myrainbowtrout, Python najpierw zadzwoni pod numer mytrout.__eq__(myrainbowtrout), zwróci uwagę, że się nie powiedzie, a następnie zadzwoni pod numer myrainbowtrout.__eq__(mytrout), który się powiedzie.

+0

Chociaż ta odpowiedź jest prawdziwa, nie wyjaśnia, w jaki sposób zaimplementować '__eq __()' w sposób, który faktycznie ułatwia powrót do obiektu '__eq __()'. –

+0

@SvenMarnach, dobry punkt. Będę edytować. – amcnabb

4

Zasada "kaczych liter" polega na tym, że nie obchodzi cię, co to jest other, o ile ma atrybut value. Więc chyba atrybuty dzielić opisy wraz sprzecznych semantyki, sugeruję zrobić to tak:

def __eq__(self, other): 
    try: 
     return self.value == other.value 
    except AttributeError: 
     return False # or whatever 

(Alternatywnie można sprawdzić, czy other posiada atrybut value, ale „łatwiej jest prosić o przebaczenie niż uzyskać zgodę”)

+3

Może to spowodować coś bardzo głupiego, jak 'Trout (" tęcza ") == Lorikeet (" tęcza ")', chociaż prawie nikt nie uważa, że ​​ryby są równe ptakom. – SingleNegationElimination

+1

@Token, w tym przypadku pisanie kaczek nie jest odpowiednie dla ciebie. Wyjaśnią się one tylko wtedy, gdy będą przechowywać "tęczę" w atrybucie "wartość". – alexis

+0

... i definiujesz ich metodę '__eq__', tak jak chce OP, aby spojrzeć tylko na pole' value'. Ale tak, to jest zasada kaczkowania. Kochaj albo rzuć. – alexis

6

Korzystanie z isintsance() jest zwykle w porządku w metodach __eq__(). Nie powinno powrócić False natychmiast jeżeli kontrola isinstance() nie powiedzie się, jeśli - to lepiej wrócić NotImplemented dać other.__eq__() szansę być wykonane:

def __eq__(self, other): 
    if isinstance(other, Trout): 
     return self.x == other.x 
    return NotImplemented 

ta będzie szczególnie ważne w hierarchii klas Jeżeli więcej niż jedno Klasa definiuje __eq__():

class A(object): 
    def __init__(self, x): 
     self.x = x 
    def __eq__(self, other): 
     if isinstance(other, A): 
      return self.x == other.x 
     return NotImplemented 
class B(A): 
    def __init__(self, x, y): 
     A.__init__(self, x) 
     self.y = y 
    def __eq__(self, other): 
     if isinstance(other, B): 
      return self.x, self.y == other.x, other.y 
     return NotImplemented 

Jeśli chcesz powrócić False natychmiast, jak to było w oryginalnym kodzie, stracą symetrię pomiędzy A(3) == B(3, 4) i B(3, 4) == A(3).

Powiązane problemy