Як перевірити (під час виконання), чи є один клас підкласом іншого?


197

Скажімо, у мене є костюм класу та чотири підкласи костюма: Heart, Spade, Diamond, Club.

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

У мене є метод, який отримує костюм як параметр, який є об'єктом класу, а не екземпляром. Точніше, він може отримати лише одне з чотирьох значень: Серце, Лопата, Алмаз, Клуб. Як я можу зробити твердження, яке забезпечує таке? Щось на зразок:

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

Я використовую Python 3.


1
@Leopd: Це справді не зрозуміло? Я точно заявив, які можливі чотири значення, які my_methodможна отримати як параметри: "воно може отримати лише одне з чотирьох значень: Heart, Spade, Diamond, Club". Ці значення - це об'єкти класу, а не екземпляри класу. Мені це здається досить зрозумілим, хоча, мабуть, ти маєш рацію щодо невиразності, оскільки відповіді охоплюють обидві можливості. Ви можете більше відредагувати питання, якщо у вас є чіткіше питання. Дякуємо за коментар
— змійка

@snakile так, незрозуміло. Завдяки покладанню на правильність чиїсь самовираження це тонка крига в цій темі. Багато новачків не можуть отримати все, що є об'єктом в пітон, можуть висловити одне, а думати інше. Це реальність і, чистоту вбік, цілком раціонально очікувати такої поведінки від новачків. Залишення своєї репутації вказує на єдиний прямий натяк на те, чи є ваше висловлювання тут правильним, чи слід сказати, «з точки зору правильності». Я розумію, що бажаю врахувати ваші знання, і все ж нераціонально не враховувати постійно оновлюваних новачків.
— n611x007

1
@snakile це, і те, що може бути розумним використовувати конвенцію про іменування, яка суфіксує такі назви параметрів _class, роблячи їх подібними suit_class. Я запропонував таку конвенцію про іменування у відповідному питанні .
— n611x007

Запропонуйте до прикладу додати чотири рядки my_method(Heart) my_method(Spade)...
— Боб Штейн

У випадках, коли тестована змінна не гарантується як клас, ви можете додати умову inspect.isclassабо просто використовувати isinstance(myvar, type)в Python 3, оскільки це issubclassпризведе до помилки, якщо вона передається некласовій. Дивіться цю відповідь . Я б прокоментував відповідь нижче, але вона ніколи не побачила б світла дня.
— тотальний хак

Відповіді:


225

Ви можете використовувати issubclass()так assert issubclass(suit, Suit).


55
"Але чому б ти хотів зробити таке?" - тому що у вас клас контейнерів, який вам потрібно забезпечити, є однорідним, і єдиний спосіб зробити це - перевірити тип при вставці?
— Адам Паркін

140
Якщо є одна річ, яка є постійною в Stack Overflow, це те, що будь-які запитання з відповіддю, що передбачає, що це речовина або issubclass, також будуть супроводжуватися лекціями про набір качок!
— Бен Робертс

26
Я зіткнувся з цим питанням, намагаючись розібратися, як визначити, чи є мій numpy dtype плаваючою чи int для програми обробки зображень. Якщо це поплавок, то умова полягає в нормалізації між 0,0 і 1,0, якщо це int, то умова становить від 0 до 255. Я можу пройти всілякі викривлення, щоб спробувати змусити зображення збиватися, але це набагато прямо вперед просто запитайте "ти качка" і відповідно масштабувати мої операції.
— Омегаман

28
Тестування підкласів значно спрощує одиничне тестування багатьох речей, зокрема моделей Django. "Python - це не Java." Чому програмісти python повинні мати такі фішки на своїх плечах?
— Майкл Бекон

20
Не вимагаючи голосування, виключно через немислені та досить зарозумілі зауваження наприкінці. Пояснення, чому людині цього не потрібно робити, було б дружнішим і кориснішим.
— Майкл Шепер


26

Ви можете використовувати, isinstanceякщо у вас є примірник або у issubclassвас клас. Зазвичай вважав це поганою ідеєю. Зазвичай в Python ви працюєте, чи об'єкт щось здатний, намагаючись зробити це.


Що робити, якщо ви дізнаєтесь, що не можете з цим зробити цього? Ви ловите виняток і пробуєте щось інше?
— неправильнекористування

2
@wrongusername: Це "піфонічний" спосіб, так. Я думаю, що цей звичай має заслугу, тому я дотримуюся його, доки він не підтримує код. Тут є хороша дискусія: stackoverflow.com/questions/7604636/…
— Майкл Шепер

8
@Michael Scheper: Я мушу сказати, якщо це пітонічний шлях, то я дуже ненавиджу пітонічний шлях. ІМО, винятки НІКОЛИ не повинні використовуватися для контролю потоку. Якщо ви думаєте, що може статися помилка, захистіть її ... не поводьтеся з нею як з GOTO. Хоча
— цікаве

@ aboveyou00: Схоже, що випадки, про які ви говорите, порушують "до тих пір, поки це не захистить мій код". Я не прихильник усіх піфонічних звичаїв, оскільки багато людей зловживають принципом EAFP і в кінцевому підсумку створюють важко знайдені помилки.
— Майкл Шепер

Ця перевірка стає корисною при визначенні договорів. Наприклад, конструктор, який отримує об'єкт: ми хотіли б стверджувати, що отриманий об’єкт є екземпляром даного класу, і тим самим повідомляємо читачеві коду, що очікує конструктор.
— mastropi

21

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


9
Відповідь без помилкової лекції +1.
— Gringo Suave

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

Ви можете використовувати вбудований issubclass. Але перевірка типу зазвичай вважається непотрібною, оскільки ви можете використовувати тип качки.


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

-5
#issubclass(child,parent)

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

print(issubclass(c,b))#it returns true

1
Відповіді, що стосуються лише коду, не оцінені в ТА, завжди слід додати в текст якийсь пояснювальний текст. Однак ця відповідь є зайвою: вона не додає жодної інформації, про яку вже не було сказано в попередніх відповідях.
— PM 2Ring
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.