Como verifico (em tempo de execução) se uma classe é uma subclasse de outra?

196

Digamos que eu tenho um naipe de classe e quatro subclasses de naipe: Coração, Pá, Diamante, Clube.

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

Eu tenho um método que recebe uma ação como parâmetro, que é um objeto de classe, não uma instância. Mais precisamente, ele pode receber apenas um dos quatro valores: Coração, Pá, Diamante, Clube. Como posso fazer uma afirmação que garanta tal coisa? Algo como:

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

Estou usando o Python 3.

snakile
fonte
1
@ Leopd: Realmente não está claro? Afirmei exatamente quais são os quatro valores possíveis que my_methodpodem ser obtidos como parâmetros: "ele pode receber apenas um dos quatro valores: Coração, Pá, Diamante, Clube". Esses valores são objetos de classe, não instâncias de classe. Parece-me bastante claro, embora eu suponha que você esteja certo sobre a imprecisão, porque as respostas cobrem as duas possibilidades. Você pode editar a pergunta se tiver uma redação mais clara. Obrigado pelo comentário.
Snakile 5/10/12
@snakile sim, não está claro. Devido à confiança na auto-expressão de alguém, há gelo fino neste tópico. Muitos recém-chegados não conseguem entender tudo o que é um objeto em python, podem expressar uma coisa, mas pensar outra. Isso é uma realidade e, pureza à parte, é bastante racional esperar esse comportamento dos recém-chegados. Deixar sua reputação aponta a única dica direta de que sua expressão aqui está correta, ou devo dizer, "em termos de correção". Entendo o desejo de levar seu conhecimento em consideração e ainda é irracional não levar em conta os recém-renovados.
N611x007
1
@snakile isso, e o que pode ser razoável usar uma convenção de nomenclatura que sufoque esses nomes de parâmetros _class, tornando-os semelhantes suit_class. I propôs tal convenção de nomenclatura em uma questão relevante .
N611x007
Sugerir a adição ao código de exemplo quatro linhas my_method(Heart) my_method(Spade)...
Bob Stein
Nos casos em que a variável que está sendo testada não é garantida como uma classe, você pode adicionar inspect.isclassou simplesmente usar uma condição isinstance(myvar, type)no Python 3, pois isso issubclassgerará um erro se ela passar por uma não-classe. Veja esta resposta . Eu teria comentado a resposta abaixo, mas nunca teria visto a luz do dia.
totalhack 20/09/19

Respostas:

224

Você pode usar issubclass()assim assert issubclass(suit, Suit).

Trevor Boyd Smith
fonte
55
"Mas por que você quer fazer uma coisa dessas?" - porque você tem uma classe de contêiner que você precisa garantir é homogênea e a única maneira de fazer isso é verificar o tipo ao inserir?
Adam Parkin
140
Se há uma coisa constante no Stack Overflow, é que todas as perguntas com uma resposta que implique em instância ou issubclass também serão acompanhadas de palestras sobre digitação de patos!
Ben Roberts
26
Me deparei com essa pergunta, tentando descobrir como detectar se meu dtype numpy é um float ou um int para um aplicativo de processamento de imagem. Se for um float, a convenção é normalizar entre 0,0 e 1,0; se for int, a convenção é de 0 a 255. Eu poderia passar por todo tipo de contorção para tentar fazer com que a imagem seja charlatanhada, mas é muito mais direto para basta perguntar "você é um pato" e escalar minhas operações de acordo.
Omegaman
28
O teste de subclasse facilita muito o teste de unidade de muitas coisas, principalmente os modelos do Django. "Python não é Java." Por que os programadores de python devem ter esses chips nos ombros?
Michael Bacon
20
Não votando, puramente por causa da observação sem imaginação e bastante arrogante no final. Uma explicação sobre o motivo pelo qual não é necessário fazer isso seria mais amigável e útil.
Michael Scheper
47

issubclass(class, classinfo)

Excerto:

Retorne true se classfor uma subclasse (direta, indireta ou virtual) de classinfo.

Katriel
fonte
26

Você pode usar isinstancese tiver uma instância ou issubclassse tiver uma classe. Normalmente, é uma má ideia. Normalmente, no Python, você descobre se um objeto é capaz de algo tentando fazer isso.

David Heffernan
fonte
E se você descobrir que não pode fazer isso com isso? Você pega uma exceção e tenta outra coisa?
wrongusername
2
@wrongusername: Essa é a maneira 'Pythonic', sim. Eu acho que esse costume tem mérito, então eu o sigo desde que mantenha meu código claro. Há uma boa discussão sobre isso aqui: stackoverflow.com/questions/7604636/...
Michael Scheper
8
@ Michael Scheper: Eu tenho que dizer, se esse é o caminho pitônico, então eu realmente odeio o caminho pitônico. IMO, NUNCA exceções devem ser usadas para controlar o fluxo. Se você acha que um erro pode ocorrer, proteja-o ... não o trate como um GOTO. Ligação interessante você postou, embora
leviathanbadger
@ aboveyou00: Parece que o tipo de casos de que você está falando viola 'desde que mantenha meu código claro'. Não sou fã de todos os costumes pitonicos, já que muitas pessoas abusam do princípio EAFP e acabam criando bugs difíceis de encontrar.
Michael Scheper
Essa verificação é útil ao definir contratos. Por exemplo, um construtor que recebe um objeto: gostaríamos de afirmar que o objeto recebido é uma instância de uma determinada classe e, ao fazer isso, informamos ao leitor de código o que o construtor espera.
mastropi 29/04
21

A issubclass(sub, sup)função booleana retorna true se a subclasse especificada subfor realmente uma subclasse da superclasse sup.

Jameer Mulani
fonte
9
A resposta sem uma palestra equivocada +1.
Gringo Suave
2

issubclass exemplo executável mínimo

Aqui está um exemplo mais completo com algumas afirmações:

#!/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 upstream .

Testado em Python 3.5.2.

Ciro Santilli adicionou uma nova foto
fonte
1

Você pode usar a issubclass interna. Mas a verificação de tipo geralmente é vista como desnecessária, porque você pode usar a digitação de pato.

XORcist
fonte
1

Usar issubclass parecia uma maneira limpa de escrever níveis de log. É meio estranho usá-lo ... mas parece mais limpo do que outras opções.

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
fonte
0

De acordo com o documento Python , também podemos usar class.__mro__atributo ou class.mro()método:

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
fonte
-5
#issubclass(child,parent)

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

print(issubclass(c,b))#it returns true
Vigneshwaran Narayanan
fonte
1
As respostas somente de código não são apreciadas no SO; você sempre deve adicionar algum texto explicativo ao código. No entanto, essa resposta é supérflua: ela não adiciona informações que ainda não foram mencionadas nas respostas anteriores.
usar o seguinte comando