Как проверить (во время выполнения), является ли один класс подклассом другого?

197

Допустим, у меня есть классный костюм и четыре подкласса костюма: Heart, Spade, Diamond, Club.

class Suit:
   ...
class Heart(Suit):
   ...
class Spade(Suit):
   ...
class Diamond(Suit):
   ...
class Club(Suit):
   ...

У меня есть метод, который получает костюм в качестве параметра, который является объектом класса, а не экземпляром. Точнее, он может получить только одно из четырех значений: Heart, Spade, Diamond, Club. Как я могу сделать утверждение, которое гарантирует такую ​​вещь? Что-то вроде:

def my_method(suit):
   assert(suit subclass of Suit)
   ...

Я использую Python 3.

snakile
источник
1
@ Leopd: это действительно не ясно? Я точно указал, какие четыре возможных значения my_methodмогут быть получены в качестве параметров: «он может получить только одно из четырех значений: Heart, Spade, Diamond, Club». Эти значения являются объектами класса, а не экземплярами класса. Мне кажется, это довольно ясно, хотя я полагаю, что вы правы насчет неопределенности, потому что ответы охватывают обе возможности. Вы можете редактировать вопрос, если у вас есть более ясное слово для этого. Спасибо за комментарий.
snakile
@ snakile да неясно. Из-за того, что полагаться на правильность чьего-либо самовыражения это тонкий лед в этой теме. Многие новички не могут понять, что все в объекте в питоне, могут выражать одно, а думать другое. Это реальность, и, кроме чистоты, вполне разумно ожидать такого поведения от новичков. Оставление вашей репутации указывает на единственный прямой намек на то, является ли ваше выражение здесь правильным или, я должен сказать, «с точки зрения правильности». Я понимаю желание принять ваши знания во внимание, и все же нерационально не принимать во внимание постоянно обновляющихся новичков.
n611x007
1
@snakile, и то, что может быть разумно использовать соглашение об именах, которое суффиксирует такие имена параметров _class, делая их похожими suit_class. Я предложил такое соглашение об именах в соответствующем вопросе .
n611x007
Предложите добавить в пример кода четыре строки my_method(Heart) my_method(Spade)...
Боб Стейн
В случаях, когда проверяемая переменная не гарантированно является классом, вы можете добавить условие inspect.isclassили просто использовать isinstance(myvar, type)в Python 3, что issubclassвызовет ошибку, если она передана не классу. Смотрите этот ответ . Я бы прокомментировал ответ ниже, но он никогда бы не увидел свет.
totalhack

Ответы:

226

Вы можете использовать issubclass()как это assert issubclass(suit, Suit).

Тревор Бойд Смит
источник
55
"Но почему ты хочешь сделать такую ​​вещь?" - потому что у вас есть контейнерный класс, который нужно обеспечить однородным, и единственный способ сделать это - проверить тип при вставке?
Адам Паркин
140
Если есть одна вещь, которая является константой переполнения стека, это то, что любые вопросы с ответом, который подразумевает isinstance или issubclass, также будут сопровождаться лекциями о типизации утки!
Бен Робертс
26
Я столкнулся с этим вопросом, пытаясь выяснить, как определить, является ли мой тип n-типа с плавающей точкой типом float или int для приложения обработки изображений. Если это число с плавающей запятой, соглашение должно нормализовать значение от 0,0 до 1,0, если это int, то соглашение от 0 до 255. Я мог бы пройти через все виды искажений, чтобы попытаться заставить изображение крякать, но гораздо проще просто спросите "ты утка" и масштабируй мои операции соответственно.
Омегаман
28
Подклассовое тестирование значительно упрощает модульное тестирование многих вещей, в частности моделей Django. «Python - это не Java». Почему программисты Python должны иметь такие чипы на своих плечах?
Майкл Бэкон
20
Не голосование, просто из-за неуместного и довольно высокомерного замечания в конце. Объяснение того, почему это не нужно делать, было бы более дружелюбным и полезным.
Майкл Шепер
26

Вы можете использовать, isinstanceесли у вас есть экземпляр, или issubclassесли у вас есть класс. Обычно считается плохой идеей. Обычно в Python вы выясняете, способен ли объект на что-то, пытаясь сделать это с ним.

Дэвид Хеффернан
источник
Что если вы обнаружите, что не можете сделать это с этим? Вы ловите исключение и пробуете что-то еще?
неправильное имя
2
@wrongusername: Да, это «Pythonic». Я думаю, что этот обычай имеет свои достоинства, поэтому я следую ему, пока он сохраняет мой код в чистоте. Здесь есть хорошее обсуждение: stackoverflow.com/questions/7604636/…
Майкл Шепер
8
@ Майкл Шепер: Я должен сказать, что если это питон, то я действительно ненавижу питон. ИМО, исключения НИКОГДА не должны использоваться для потока управления. Если вы думаете, что может произойти ошибка, защититесь от нее ... не относитесь к ней как к GOTO. Интересная ссылка , которую вы в курсе, хотя
leviathanbadger
@ aboveyou00: Похоже, случаи, о которых вы говорите, нарушают «до тех пор, пока мой код остается чистым». Однако я не фанат всех питоновских обычаев, поскольку многие люди злоупотребляют принципом EAFP и в конечном итоге создают трудно обнаруживаемые ошибки.
Майкл Шепер
Эта проверка полезна при определении контрактов. Например, конструктор, который получает объект: мы хотели бы утверждать, что полученный объект является экземпляром данного класса, и тем самым мы информируем читателя кода о том, что ожидает конструктор.
мастропи
21

issubclass(sub, sup)Булева функция возвращает истину , если данный подкласс subдействительно подкласс суперкласса sup.

Jameer Mulani
источник
9
Ответ без ошибочной лекции +1.
Гринго Суаве
2

issubclass минимальный работоспособный пример

Вот более полный пример с некоторыми утверждениями:

#!/usr/bin/env python3

class Base:
    pass

class Derived(Base):
    pass

base = Base()
derived = Derived()

# Basic usage.
assert issubclass(Derived, Base)
assert not issubclass(Base, Derived)

# True for same object.
assert issubclass(Base, Base)

# Cannot use object of class.
try:
    issubclass(derived, Base)
except TypeError:
    pass
else:
    assert False

# Do this instead.
assert isinstance(derived, Base)

GitHub вверх по течению .

Протестировано в Python 3.5.2.

Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
1

Вы можете использовать встроенный класс. Но проверка типов обычно рассматривается как ненужная, потому что вы можете использовать типизацию по типу утки.

XORcist
источник
1

Использование issubclass казалось чистым способом записи уровней журналов. Это немного странно при использовании ... но кажется чище, чем другие варианты.

class Error(object): pass
class Warn(Error): pass
class Info(Warn): pass
class Debug(Info): pass

class Logger():
    LEVEL = Info

    @staticmethod
    def log(text,level):
        if issubclass(Logger.LEVEL,level):
            print(text)
    @staticmethod
    def debug(text):
        Logger.log(text,Debug)   
    @staticmethod
    def info(text):
        Logger.log(text,Info)
    @staticmethod
    def warn(text):
        Logger.log(text,Warn)
    @staticmethod
    def error(text):
        Logger.log(text,Error)
Джон Доннелли
источник
0

Согласно документации по Python , мы также можем использовать class.__mro__атрибут или class.mro()метод:

class Suit:
    pass
class Heart(Suit):
    pass
class Spade(Suit):
    pass
class Diamond(Suit):
    pass
class Club(Suit):
    pass

>>> Heart.mro()
[<class '__main__.Heart'>, <class '__main__.Suit'>, <class 'object'>]
>>> Heart.__mro__
(<class '__main__.Heart'>, <class '__main__.Suit'>, <class 'object'>)

Suit in Heart.mro()  # True
object in Heart.__mro__  # True
Spade in Heart.mro()  # False
YaOzI
источник
-5
#issubclass(child,parent)

class a:
    pass
class b(a):
    pass
class c(b):
    pass

print(issubclass(c,b))#it returns true
Виньешваран Нараянан
источник
1
Ответы только на код не приветствуются в SO, всегда следует добавлять пояснительный текст к коду. Однако этот ответ является излишним: он не добавляет никакой информации, которая не была упомянута в предыдущих ответах.
PM 2Ring 13.09.16