Проверьте, принадлежат ли экземпляры к одному и тому же классу

#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. @Фантом нет, это не так.