Come posso verificare (in fase di esecuzione) se una classe è una sottoclasse di un'altra?


197

Diciamo che ho un seme di classe e quattro sottoclassi di seme: Heart, Spade, Diamond, Club.

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

Ho un metodo che riceve un seme come parametro, che è un oggetto di classe, non un'istanza. Più precisamente, può ricevere solo uno dei quattro valori: Heart, Spade, Diamond, Club. Come posso fare un'affermazione che garantisca una cosa del genere? Qualcosa di simile a:

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

Sto usando Python 3.


1
@Leopd: non è davvero chiaro? Ho affermato esattamente quali sono i quattro valori my_methodpossibili che possono ottenere come parametri: "può ricevere solo uno dei quattro valori: Cuore, Vanga, Diamante, Club". Questi valori sono oggetti di classe, non istanze di classe. Mi sembra abbastanza chiaro, anche se suppongo che tu abbia ragione sulla vaghezza perché le risposte coprono entrambe le possibilità. Sei più che benvenuto a modificare la domanda se hai un testo più chiaro per questo. Grazie per il commento.
snakile,

@snakile sì, non è chiaro. A causa del fatto di fare affidamento sulla correttezza dell'espressione personale di chiunque, in questo argomento è un problema. Molti nuovi arrivati ​​non riescono a ottenere tutto ciò che è un oggetto in pitone, possono esprimere una cosa ma pensarne un'altra. Questa è una realtà e, a parte la purezza, è abbastanza razionale aspettarsi questo comportamento dai nuovi arrivati. Lasciare la tua reputazione indica l'unico suggerimento diretto se la tua espressione qui è corretta, o dovrei dire "in termini di correttezza". Comprendo il desiderio di prendere in considerazione le tue conoscenze ed è ancora irrazionale non prendere in considerazione i nuovi arrivati ​​in costante rinnovamento.
n611x007,

1
@snakile that, e la cosa che può essere ragionevole usare una convenzione di denominazione che suffissi tali nomi di parametri _class, rendendoli simili suit_class. Ho proposto una simile convenzione di denominazione in una domanda pertinente .
n611x007,

Suggerisci di aggiungere al codice di esempio quattro righe my_method(Heart) my_method(Spade)...
Bob Stein,

Per i casi in cui la variabile in fase di test non è garantita come classe, è possibile aggiungere una condizione inspect.isclasso semplicemente utilizzarla isinstance(myvar, type)in Python 3, poiché issubclassgenererà un errore se viene passata una non classe. Vedere questo risposta . Avrei commentato la risposta qui sotto, ma non avrebbe mai visto la luce del giorno.
totalhack,

Risposte:


226

Puoi usare issubclass()così assert issubclass(suit, Suit).


55
"Ma perché vorresti fare una cosa del genere?" - perché hai una classe di container che devi garantire è omogenea e l'unico modo per farlo è controllare il tipo al momento dell'inserimento?
Adam Parkin,

140
Se c'è una cosa che è una costante su Stack Overflow, è che qualsiasi domanda con una risposta che implica isinstance o issubclass sarà anche accompagnata da lezioni sulla tipizzazione delle anatre!
Ben Roberts,

26
Mi sono imbattuto in questa domanda cercando di capire come rilevare se il mio dtype intorpidito è un float o un int per un'applicazione di elaborazione delle immagini. Se si tratta di un float, la convenzione deve normalizzarsi tra 0,0 e 1,0, se è int la convenzione è compresa tra 0 e 255. Potrei passare attraverso tutti i tipi di contorsioni per provare a far cullare l'immagine, ma è molto più semplice chiedi semplicemente "sei un papero" e scala le mie operazioni di conseguenza.
Omegaman,

28
I test delle sottoclassi rendono molto più semplici i test unitari di molte cose, in particolare i modelli di Django. "Python non è Java." Perché i programmatori di Python devono avere tali chip sulle spalle?
Michael Bacon,

20
Non votazione, puramente a causa dell'osservazione priva di fantasia e piuttosto arrogante alla fine. Una spiegazione del motivo per cui non potrebbe essere necessario farlo sarebbe più amichevole e più utile.
Michael Scheper,


26

Puoi usare isinstancese hai un'istanza o issubclassse hai una classe. Normalmente ho pensato che fosse una cattiva idea. Normalmente in Python si capisce se un oggetto è capace di qualcosa tentando di fargli quella cosa.


E se scoprissi che non puoi farci quella cosa? Prendi un'eccezione e prova qualcos'altro?
wrongusername

2
@wrongusername: questo è il modo 'Pythonic', sì. Penso che questa usanza abbia un merito, quindi la seguo finché mantiene il mio codice chiaro. C'è una buona discussione al riguardo qui: stackoverflow.com/questions/7604636/…
Michael Scheper

8
@Michael Scheper: Devo dire che, se questo è il modo pitonico, allora odio davvero il modo pitonico. IMO, le eccezioni non devono MAI essere utilizzate per il flusso di controllo. Se pensi che possa verificarsi un errore, proteggilo da esso ... non trattarlo come un GOTO. Link interessante che hai pubblicato, tuttavia
leviathanbadger

@ aboveyou00: sembra il tipo di casi di cui stai parlando violano "purché mantenga il mio codice chiaro". Non sono un fan di tutte le usanze di Pythonic, dal momento che molte persone abusano del principio EAFP e finiscono per creare bug difficili da trovare.
Michael Scheper,

Questo controllo è utile quando si definiscono i contratti. Ad esempio, un costruttore che riceve un oggetto: vorremmo affermare che l'oggetto ricevuto è un'istanza di una determinata classe, e così facendo informiamo il lettore di codice cosa si aspetta il costruttore.
Mastropi

21

La issubclass(sub, sup)funzione booleana ritorna vera se la sottoclasse data subè effettivamente una sottoclasse della superclasse sup.


9
La risposta senza una lezione sbagliata +1.
Gringo Suave,

2

issubclass esempio minimo eseguibile

Ecco un esempio più completo con alcune asserzioni:

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

Testato in Python 3.5.2.


1

È possibile utilizzare la issubclass integrata. Ma il controllo del tipo è di solito visto come inutile perché puoi usare la tipizzazione anatra.


1

L'uso di issubclass sembrava un modo chiaro per scrivere i livelli di Google. Sembra strano usarlo ... ma sembra più pulito di altre opzioni.

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

Secondo il documento Python , possiamo anche usare l' class.__mro__attributo o il class.mro()metodo:

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
Le risposte di solo codice non sono apprezzate su SO, dovresti sempre aggiungere del testo esplicativo al codice. Tuttavia, questa risposta è superflua: non aggiunge informazioni che non sono già state menzionate nelle risposte precedenti.
PM 2Ring
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.