#python #polymorphism #instance
#питон #полиморфизм #пример
Вопрос:
У меня есть основное домашнее животное «абстрактного» класса и два «реальных» класса-Собака и Кошка. Когда у меня есть два экземпляра домашних животных, я хочу знать, являются ли они «одинаковыми домашними животными», не заботясь о том, какие они домашние животные.
Я попробовал это
#!/usr/bin/python class Pet: name = "pet" def __eq__(self, other): return type(self) == type(other) # return type(self).__name__ == type(other).__name__ # return self.__class__ == other.__class__ # return self.__class__.__name__ == other.__class__.__name__ # return self.__class__ is other.__class__ # return self.__class__.__name__ is other.__class__.__name__ # return isinstance(other, type(self).__name__) # return isinstance(other, type(self)) # return isinstance(self, type(other).__name__) # return isinstance(self, type(other)) # return type(self) is type(other) # return type(self).__name__ is type(other).__name__ class Dog: name = "dog" class Cat: name = "cat" dog1 = Dog() dog2 = Dog() cat1 = Cat() cat2 = Cat() print("dog1 == dog2: {0}".format(dog1 == dog2)) print("cat1 == cat2: {0}".format(cat1 == cat2)) print("cat1 == dog1: {0}".format(cat1 == dog1))
Я перепробовал все прокомментированные возвраты, и это всегда дает такой результат:
$ ./test.py dog1 == dog2: False cat1 == cat2: False cat1 == dog1: False
Я думаю, это потому, что он рассматривает первый операнд как a Pet
, потому что метод находится в этом классе.
Есть ли способ провести такой тест в основном классе, чтобы избежать дублирования кода?
РЕДАКТИРОВАТЬ:
Как упоминали несколько человек, он забыл часть подклассов…
Это работает так, как задумывалось
#!/usr/bin/python class Pet: name = "pet" def __eq__(self, other): return self.__class__ == other.__class__ class Dog(Pet): name = "dog" class Cat(Pet): name = "cat"
Комментарии:
1. Вы намеревались создать
Dog
подклассы иCat
подклассыPet
?Pet
в настоящее время не имеет цели в этом кодексе.2. Прекратите использовать
class.__name__
3. Почему бы и нет dog1.name ==cat1.name ? Вы также можете сделать тип(dog1) == тип(cat1)
4. Это также более читабельно, если вы используете f-строки вместо
format
.5. Если вы не забудете о подклассах, ваш код будет работать. Определите свои классы как
class Dog(Pet):
иclass Cat(Pet):
. И самое главное: кошка всегда приходит раньше собаки (говорит моя кошка).
Ответ №1:
Один из способов-использовать name
параметр, определенный в приведенном выше коде. Кроме того, Dog
amp; Cat
необходимо наследовать Pet
, чтобы они могли использовать определенную функцию эквалайзера.
#!/usr/bin/python class Pet: name = "pet" def __eq__(self, other): return self.name == other.name class Dog(Pet): name = "dog" class Cat(Pet): name = "cat" dog1 = Dog() dog2 = Dog() cat1 = Cat() cat2 = Cat() print("dog1 == dog2: {0}".format(dog1 == dog2)) print("cat1 == cat2: {0}".format(cat1 == cat2)) print("cat1 == dog1: {0}".format(cat1 == dog1))
Ответ №2:
Здесь вы можете использовать 3 подхода, каждый из которых приводит к тонким различиям в поведении, в частности, в отношении любых будущих подклассов, которые вы можете создать.
Во всех 3 случаях общий код должен выглядеть следующим образом. Я взял на себя смелость добавить класс, чтобы проиллюстрировать некоторые интересные моменты :
from abc import ABC, abstractmethod class Pet(ABC): name: str def __eq__(self, other): ??? @abstractmethod def __init__(self): pass class Dog(Pet): name = "dog" def __init__(self): pass class BadDog(Dog): def __init__(self): pass class Cat(Pet): name = "cat" def __init__(self): pass class UglyKitty(Cat): name = "ugly-kitty" def __init__(self): pass
Теперь давайте рассмотрим 3 вещи, которые мы можем заменить ???
return type(self) is type(other)
Это означает, что 2 объекта будут оцениваться как равные тогда и только тогда, когда они относятся к одному и тому же типу. В этом сценарии an UglyKitty
не рассматривается как a Cat
. Аналогично для BadDog
и. Dog
return isinstance(self, other.__class__) or isinstance(other, self.__class__)
Это означает, что 2 объекта будут оцениваться как равные, если один из них относится к одному классу или подклассу другого. Другими словами, в этом случае an UglyKitty
считается равным a Cat
, но оно не равно a Dog
. Аналогично, а BadDog
есть а Dog
, но это не а Cat
. В связи с этим следует отметить, что разумное использование ABC
и abstractmethod
показанное выше гарантирует, что вы никогда напрямую не создадите экземпляр Pet
, который будет считаться равным любому другому животному.
return self.name == other.name
В этом сценарии нет гарантированного и универсального поведения. Теперь у вас есть прекрасный контроль над тем, что равно чему, за счет необходимости вручную выбирать эти равенства. Это достигается путем выбора name
атрибута, соответствующего тому, что вы хотите. В нашем случае a BadDog
равно a Dog
(поскольку мы не изменили его name
атрибут и поэтому он наследует его от Dog
), но an UglyKitty
не является a Cat
.
Как вы можете видеть, разные решения приводят к разному поведению. Теперь все, что вам нужно сделать, это решить, какое поведение вы на самом деле хотите.
Ответ №3:
Вы можете использовать class.__name__
для этого:
dog1__name__ == dog2.__name__
Обновить
Как говорит @Tbaki, вы type()
также можете использовать:
type(dog1) == type(dog2)
Еще Одно Обновление
Так будет лучше:
type(dog1) is type(dog2) # notice that I use 'is'
Комментарии:
1. Не лучше ли было бы проверить
type(dog1) is type(dog2)
? Потому что нас интересует, принадлежат ли два экземпляра к одному и тому же классу.2. Ага. Так будет лучше.
3. На самом деле, в методе в классе Pet с
type(...) is type(...)
иtype(...) == type(...)
дают Истину дляcat1 == dog1
, в то время как этого не должно быть. Сself.__class
ним все работает правильно4. Не используйте
dog1__name__ == dog2.__name__
5. @Фантом нет, это не так.