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


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.