Если в Python написано "утка", стоит ли тестировать isinstance? - PullRequest
16 голосов
/ 23 марта 2012

У вас есть класс Python, который нуждается в тесте equals.Python должен использовать Duck-typing, но стоит ли (лучше / точнее) включать или исключать тест isinstance в функции eq ?Например:

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

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

Ответы [ 3 ]

13 голосов
/ 23 марта 2012

Использование isinstance в __eq__ методах довольно распространено. Причина этого заключается в том, что в случае сбоя метода __eq__ он может откатиться на метод __eq__ из другого объекта. Большинство нормальных методов вызываются явно, но __eq__ вызывается неявно, поэтому требуется более частый просмотр.

РЕДАКТИРОВАТЬ (спасибо за напоминание, Свен Марнах):

Чтобы сделать его запасным, вы можете вернуть синглтон NotImplemented, как в этом примере:

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

Предположим, что RainbowTrout знает, как сравнивать себя с Trout или другим RainbowTrout, но Trout знает, как сравнивать себя с Trout. В этом примере, если вы тестируете mytrout == myrainbowtrout, Python сначала вызовет mytrout.__eq__(myrainbowtrout), заметит, что произошел сбой, а затем вызовет myrainbowtrout.__eq__(mytrout), что успешно.

6 голосов
/ 23 марта 2012

Использование isintsance() обычно хорошо в __eq__() методах.Вы не должны возвращать False немедленно, если проверка isinstance() не удалась, однако лучше вернуть NotImplemented, чтобы дать other.__eq__() шанс выполнения:

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

Это будетстановятся особенно важными в иерархиях классов, где более чем один класс определяет __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

Если вы сразу же вернете False, как в исходном коде, вы потеряете симметрию между A(3) == B(3, 4)и B(3, 4) == A(3).

4 голосов
/ 23 марта 2012

Принцип "типизирования утки" заключается в том, что вам все равно, что такое other, если оно имеет атрибут value. Поэтому, если ваши атрибуты не имеют общих имен с конфликтующей семантикой, я бы предложил сделать это так:

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

(В качестве альтернативы вы можете проверить, есть ли у other атрибут value, но «проще попросить прощения, чем получить разрешение»)

...