Jak sprawdzić (w czasie wykonywania), czy jedna klasa jest podklasą innej?

196

Powiedzmy, że mam garnitur klasy i cztery podklasy garnituru: Serce, Szpadel, Diament, Klub.

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

Mam metodę, która odbiera kolor jako parametr, który jest obiektem klasy, a nie instancją. Dokładniej, może otrzymać tylko jedną z czterech wartości: Serce, Szpadel, Diament, Klub. Jak mogę złożyć twierdzenie, które zapewni takie rozwiązanie? Coś jak:

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

Używam Python 3.

snakile
źródło
1
@Leopd: Czy to naprawdę nie jest jasne? Podałem dokładnie, jakie są możliwe cztery wartości, które my_methodmożna uzyskać jako parametry: „może otrzymać tylko jedną z czterech wartości: Serce, Szpadel, Diament, Klub”. Te wartości są obiektami klasy, a nie instancjami klasy. Wydaje mi się to dość jasne, choć przypuszczam, że masz rację co do niejasności, ponieważ odpowiedzi obejmują obie możliwości. Z przyjemnością edytujesz pytanie, jeśli masz na to wyraźniejsze sformułowanie. Dziękuję za komentarz.
snakile
@snakile tak, to jest niejasne. Z powodu polegania na poprawności czyjegoś wyrażania siebie w tym temacie jest cienki lód. Wielu nowicjuszy nie może dostać wszystkiego, co jest pytanym obiektem, może wyrazić jedną rzecz, ale pomyśleć inaczej. To rzeczywistość i, pomijając czystość, racjonalne jest oczekiwanie takiego zachowania od nowo przybyłych. Pozostawienie swojej reputacji wskazuje na jedyną bezpośrednią wskazówkę, czy twoje wyrażenie tutaj jest poprawne, czy raczej powiedzieć „pod względem poprawności”. Rozumiem, że chcesz wziąć pod uwagę twoją wiedzę i nadal irracjonalne jest nie brać pod uwagę stale odnawiających się przybyszów.
n611x007
1
@snakile that, a rzeczą, która może być uzasadniona, jest stosowanie konwencji nazewnictwa, która dodaje takie nazwy parametrów _class, czyniąc je podobnymi suit_class. I zaproponował taką konwencję nazywania w odpowiednim pytaniem .
n611x007
Zaproponuj dodanie do przykładowego kodu czterech linii my_method(Heart) my_method(Spade)...
Bob Stein,
W przypadkach, w których testowana zmienna nie jest klasą, możesz dodać warunek inspect.isclasslub po prostu użyć go isinstance(myvar, type)w Pythonie 3, ponieważ issubclassspowoduje to błąd, jeśli zostanie przekazany nieklasowy. Zobacz tę odpowiedź . Skomentowałbym odpowiedź poniżej, ale nigdy nie ujrzałaby światła dziennego.
totalhack 20.09.19

Odpowiedzi:

224

Możesz użyć w issubclass()ten sposób assert issubclass(suit, Suit).

Trevor Boyd Smith
źródło
55
„Ale dlaczego miałbyś chcieć coś takiego zrobić?” - ponieważ masz klasę kontenera, którą musisz upewnić się, że jest jednorodna, a jedynym sposobem na to jest sprawdzenie typu po wstawieniu?
Adam Parkin
140
Jeśli jest jedna rzecz, która jest stała w przepełnieniu stosu, jest to, że każdemu pytaniu z odpowiedzią, która sugeruje isinstance lub issclcl, będą również towarzyszyć wykłady o pisaniu kaczek!
Ben Roberts,
26
Natknąłem się na to pytanie, próbując dowiedzieć się, jak wykryć, czy mój typ numpy jest zmiennoprzecinkowy czy int dla aplikacji do przetwarzania obrazu. Jeśli jest to liczba zmiennoprzecinkowa, konwencja ma normalizować się między 0,0 a 1,0, jeśli jest int, to konwencja wynosi od 0 do 255. Mógłbym przejść przez różnego rodzaju zniekształcenia, aby spróbować uzyskać obraz, który będzie kwakać, ale jest to o wiele prostsze po prostu zapytaj „jesteś kaczką” i odpowiednio skaluj moje operacje.
Omegaman
28
Testy podklasy znacznie ułatwiają testowanie jednostkowe wielu rzeczy, szczególnie modeli Django. „Python to nie Java.” Dlaczego programiści python muszą mieć takie układy na swoich ramionach?
Michael Bacon
20
Nie entuzjastyczne, wyłącznie z powodu niewyobrażalnej i raczej aroganckiej uwagi na końcu. Wyjaśnienie, dlaczego nie trzeba tego robić, byłoby bardziej przyjazne i pomocne.
Michael Scheper,
47

issubclass(class, classinfo)

Fragment:

Zwraca true, jeśli classjest podklasą (bezpośrednią, pośrednią lub wirtualną) dla classinfo.

Katriel
źródło
26

Możesz użyć, isinstancejeśli masz instancję lub issubclassklasę. Zwykle uważał to za zły pomysł. Zwykle w Pythonie sprawdzasz, czy obiekt jest zdolny do czegoś, próbując to zrobić.

David Heffernan
źródło
Co jeśli dowiesz się, że nie możesz tego zrobić? Czy łapiesz wyjątek i próbujesz czegoś innego?
wrongusername 20.04.16
2
@wrongusername: Tak, to jest „pythoniczny” sposób. Myślę, że ten zwyczaj ma swoje zalety, więc przestrzegam go tak długo, jak długo mój kod jest czysty. Dobra dyskusja na ten temat tutaj: stackoverflow.com/questions/7604636/…
Michael Scheper
8
@Michael Scheper: Muszę powiedzieć, że jeśli jest to sposób pytoniczny, to naprawdę nienawidzę sposobu pytonicznego. IMO, NIGDY nie należy NIGDY stosować do kontroli przepływu. Jeśli uważasz, że może wystąpić błąd, chroń się przed nim ... nie traktuj go jak GOTO. Ciekawy link, który opublikowałeś
leviathanbadger
@ aboveyou00: Wygląda na to, że przypadki, o których mówisz, naruszają „tak długo, jak utrzymuje mój kod w czystości”. Jednak nie jestem fanem wszystkich Pythońskich zwyczajów, ponieważ wiele osób narusza zasadę EAFP i w rezultacie tworzą trudne do znalezienia błędy.
Michael Scheper,
Ta kontrola jest przydatna podczas definiowania umów. Na przykład konstruktor, który odbiera obiekt: chcielibyśmy stwierdzić, że otrzymany obiekt jest instancją danej klasy, a tym samym informujemy czytnik kodu, czego oczekuje konstruktor.
mastropi
21

Funkcja issubclass(sub, sup)boolowska zwraca true, jeśli podklasa subjest rzeczywiście podklasą nadklasy sup.

Jameer Mulani
źródło
9
Odpowiedź bez błędnego wykładu +1.
Gringo Suave
2

issubclass minimalny możliwy do uruchomienia przykład

Oto bardziej kompletny przykład z pewnymi twierdzeniami:

#!/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 w górę .

Testowane w języku Python 3.5.2.

Ciro Santilli
źródło
1

Możesz użyć wbudowanej podklasy. Ale sprawdzanie typu jest zwykle postrzegane jako niepotrzebne, ponieważ można używać pisania kaczego.

XORcist
źródło
1

Korzystanie z Issubclass wydawało się czystym sposobem pisania loglevels. To trochę dziwne, używając go ... ale wydaje się czystsze niż inne opcje.

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)
Jon Donnelly
źródło
0

Zgodnie z dokumentacją Pythona możemy również użyć class.__mro__atrybutu lub class.mro()metody:

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
źródło
-5
#issubclass(child,parent)

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

print(issubclass(c,b))#it returns true
Vigneshwaran Narayanan
źródło
1
Odpowiedzi tylko w kodzie nie są doceniane w SO, więc zawsze powinieneś dodać do kodu jakiś objaśniający tekst. Jednak ta odpowiedź jest zbyteczna: nie dodaje informacji, które nie zostały wspomniane w poprzednich odpowiedziach.
PM 2Ring