¿Cómo verifico (en tiempo de ejecución) si una clase es una subclase de otra?

196

Digamos que tengo un traje de clase y cuatro subclases de traje: Corazón, Espada, Diamante, Club.

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

Tengo un método que recibe un traje como parámetro, que es un objeto de clase, no una instancia. Más precisamente, puede recibir solo uno de los cuatro valores: Corazón, Espada, Diamante, Club. ¿Cómo puedo hacer una afirmación que garantice tal cosa? Algo como:

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

Estoy usando Python 3.

snakile
fuente
1
@Leopd: ¿Realmente no está claro? He indicado exactamente cuáles son los cuatro valores posibles que se my_methodpueden obtener como parámetros: "puede recibir solo uno de los cuatro valores: Corazón, Espada, Diamante, Club". Esos valores son objetos de clase, no instancias de clase. Me parece bastante claro, aunque supongo que tienes razón acerca de la vaguedad porque las respuestas cubren ambas posibilidades. Eres más que bienvenido a editar la pregunta si tienes un lenguaje más claro para ello. Gracias por el comentario.
Snakile
@snakile sí, no está claro. Debido a depender de la corrección de la autoexpresión de cualquier persona, este tema es muy escaso. Muchos recién llegados no pueden obtener todo lo que es un objeto en Python, pueden expresar una cosa pero pensar en otra. Esa es una realidad y, aparte de la pureza, es bastante racional esperar este comportamiento de los recién llegados. Dejar su reputación señala la única pista directa de si su expresión aquí es correcta, o debería decir, "en términos de corrección". Entiendo el deseo de tener en cuenta su conocimiento y sigue siendo irracional no tener en cuenta a los recién llegados que se renuevan.
n611x007
1
@snakile eso, y lo que puede ser razonable usar una convención de nomenclatura con sufijos de tales nombres de parámetros _class, haciéndolos gustar suit_class. Me propuse dicha convención una denominación en una cuestión relevante .
n611x007
Sugiera agregar al código de ejemplo cuatro líneas my_method(Heart) my_method(Spade)...
Bob Stein
Para los casos en los que no se garantiza que la variable que se está probando sea una clase, puede agregar una condición inspect.isclasso simplemente usarla isinstance(myvar, type)en Python 3, ya que issubclassgenerará un error si se pasa una no clase. Mira esta respuesta . Hubiera comentado la respuesta a continuación, pero nunca habría visto la luz del día.
totalhack

Respuestas:

224

Puedes usar issubclass()así assert issubclass(suit, Suit).

Trevor Boyd Smith
fuente
55
"¿Pero por qué querrías hacer algo así?" - porque tiene una clase de contenedor que necesita asegurarse de que sea homogénea, y la única forma de hacerlo es verificar el tipo al insertarlo.
Adam Parkin
140
Si hay una cosa que es constante en Stack Overflow, es que cualquier pregunta con una respuesta que implique una instancia o una subclase también se acompañará de conferencias sobre tipeo de patos.
Ben Roberts
26
Encontré esta pregunta tratando de descubrir cómo detectar si mi dtype numpy es flotante o int para una aplicación de procesamiento de imágenes. Si es flotante, la convención es normalizar entre 0.0 y 1.0, si es int, entonces la convención es de 0 a 255. Podría pasar por todo tipo de contorsiones para intentar que la imagen grazne, pero es mucho más sencillo solo pregunte "eres un pato" y escala mis operaciones en consecuencia.
Omegaman
28
Las pruebas de subclase hacen que las pruebas unitarias de muchas cosas, particularmente los modelos de Django, sean mucho más fáciles. "Python no es Java". ¿Por qué los programadores de Python deben tener esos chips sobre sus hombros?
Michael Bacon
20
No votar, simplemente por el comentario poco imaginativo y bastante arrogante al final. Una explicación sobre por qué uno no necesita hacer esto sería más amigable y más útil.
Michael Scheper
26

Puede usar isinstancesi tiene una instancia, o issubclasssi tiene una clase. Normalmente pensé que era una mala idea. Normalmente, en Python se determina si un objeto es capaz de hacer algo al intentar hacerle eso.

David Heffernan
fuente
¿Qué pasa si descubres que no puedes hacer eso con él? ¿Capturas una excepción y pruebas algo más?
wrongusername
2
@wrongusername: Esa es la forma 'Pitónica', sí. Creo que esta costumbre tiene mérito, así que la sigo siempre que mantenga mi código claro. Aquí hay una buena discusión sobre esto: stackoverflow.com/questions/7604636/…
Michael Scheper
8
@Michael Scheper: Tengo que decir que si esa es la forma pitónica, entonces realmente odio la forma pitónica. En mi opinión, las excepciones NUNCA deben usarse para controlar el flujo. Si cree que puede ocurrir un error, protéjase contra él ... no lo trate como un GOTO. Sin embargo, un enlace interesante que publicaste
leviathanbadger
@ aboveyou00: Parece que el tipo de casos de los que estás hablando viola "siempre que mantenga mi código claro". Sin embargo, no soy fanático de todas las costumbres pitónicas, ya que muchas personas abusan del principio EAFP y terminan creando errores difíciles de encontrar.
Michael Scheper el
Este control es útil cuando se definen contratos. Por ejemplo, un constructor que recibe un objeto: nos gustaría afirmar que el objeto recibido es una instancia de una clase dada, y al hacerlo, informamos al lector de código qué espera el constructor.
mastropi
21

La issubclass(sub, sup)función booleana devuelve verdadero si la subclase dada subes de hecho una subclase de la superclase sup.

Jameer Mulani
fuente
9
La respuesta sin una lectura equivocada +1.
Gringo Suave
2

issubclass ejemplo ejecutable mínimo

Aquí hay un ejemplo más completo con algunas afirmaciones:

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

Probado en Python 3.5.2.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
1

Puede usar la subclase incorporada. Pero la verificación de tipos generalmente se considera innecesaria porque puede usar la escritura de pato.

XORcist
fuente
1

Usar issubclass parecía una forma limpia de escribir loglevels. Se siente un poco extraño usarlo ... pero parece más limpio que otras opciones.

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

Según el documento de Python , también podemos usar el class.__mro__atributo o el 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
fuente
-5
#issubclass(child,parent)

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

print(issubclass(c,b))#it returns true
Vigneshwaran Narayanan
fuente
1
Las respuestas de solo código no son apreciadas en SO, siempre debe agregar algún texto explicativo al código. Sin embargo, esta respuesta es superflua: no agrega información que no se haya mencionado en las respuestas anteriores.
PM 2Ring