How do I check (at runtime) if one class is a subclass of another?
Asked Answered
N

10

266

Let's say that I have a class Suit and four subclasses of suit: Heart, Spade, Diamond, Club.

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

I have a method which receives a suit as a parameter, which is a class object, not an instance. More precisely, it may receive only one of the four values: Heart, Spade, Diamond, Club. How can I make an assertion which ensures such a thing? Something like:

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

I'm using Python 3.

Nuncia answered 6/2, 2011 at 11:26 Comment(6)
@Leopd: Is it really not clear? I have stated exactly what are the possible four values which my_method can get as parameters: "it may receive only one of the four values: Heart, Spade, Diamond, Club". Those values are class objects, not class instances. It seems pretty clear to me, though I suppose you're right about the vagueness because the answers do cover both possibilities. You're more than welcome to edit the question if you've got a clearer wordage for it. Thanks for the comment.Nuncia
@Nuncia yes it is unclear. Due to relying on the correctness of anyone's self-expression is thin ice in this topic. Many newcomers can't get the everything-is-an-object-in-python thing, may express one thing but think another. That's a reality and, purity aside, it's quite rational to expect this behavior from newcomers. Leaving your reputation points the only direct hint whether your expression here is correct, or should I say, "in terms of correctness". I understand the wish to take your knowledge into account and it's still irrational not to take into account the ever-renewing newcomers.Contemptuous
@Nuncia that, and the thing that it may be reasonable to use a naming convention that suffixes such parameter names with _class, making them like suit_class. I proposed such a naming convention in a relevant question.Contemptuous
Suggest adding to the example code four lines my_method(Heart) my_method(Spade) ...Catheryncatheter
For cases where the variable being tested is not guaranteed to be a class, you can add a condition on inspect.isclass or simply use isinstance(myvar, type) in Python 3, as issubclass will raise an error if it's passed a non-class. See this answer. I would have commented on the answer below, but it never would have seen the light of day.Coadunate
The question is inaccurate. It rather asks if an object is of a type that is a subclass of another. This is different from a type being subclass of another directly. If you have a type, the correct answer doesn't apply. Python naming doesn't help either, using instance and (sub)class for methods with the same signature. I cannot propose editing it because the queue is full.Ibby
N
320

You can use issubclass() like this assert issubclass(suit, Suit).

Nagoya answered 6/2, 2011 at 11:31 Comment(19)
You've changed my answer from isinstance to issubclass. You're really passing classes rather than objects around?Nagoya
Yes. Each suit is a class, a subclass of Suit. What I want here is four possible values. There are no instances of Suit and there are no instances of Heart, Spade, Club or Diamond. They're singletons. Is that a bad design?Nuncia
"But why would you want to do such a thing?" -- because you have a container class that you need to ensure is homogeneous, and the only way to do that is to check the type upon insert?Slipover
I have a test that ensures my handlers are subclasses of BaseHandler, and that they are not strings. (where they may be lazily imported later)Fluoroscopy
If there's one thing that's a constant on Stack Overflow, it is that any questions with an answer that implies isinstance or issubclass will also be accompanied with lectures about duck typing!Whereas
On the why you would want to do that part. For command line programs I use issubclass against a module's attributes to buildup simple commandline interfaces "my_program.py <list of valid subclasses to call>", so if I've got my_module.Foo(Command) being collected, you could call my_program Foo and it would through introspection call Foo.Run()Hevesy
I came across this question trying to figure out how to detect if my numpy dtype is a float or an int for an image processing application. If it's a float, the convention is to normalize between 0.0 and 1.0, if it's int then the convention is 0 to 255. I could go through all sorts of contortions to try and get the image to quack, but it's much more straight forward to just ask "are you a duck" and scale my operations accordingly.Miscible
A thing to keep in mind: A class is considered a subclass of itself. docs.python.org/2/library/functions.html#issubclassPredecease
Duck typing doesn't remove use cases for this. Take, for example, Django's signal framework. send_robust returns a tuple of (receiver, response), where response might be the receiver's return value, or it might be an Exception subclass. Using issubclass() to determine which is a perfectly valid use case.Gourmandise
Subclass testing makes unit testing of many things, particularly Django's models, much easier. "Python is not Java." Why must python programmers have such chips on their shoulders?Dietitian
Not upvoting, purely because of the unimaginative and rather arrogant remark at the end. An explanation about why one might not need to do this would be friendlier and more helpful.Improvement
Guess isinstance is enough for most use cases.Concessive
if issubclass(type(e), Exception): logger.exception(...) else: logger.error(...)Ranunculus
Can someone explain me "Python is not Java" remark? Can't figure out what does this mean :(Firebreak
@AlexT The idea behind python's "duck typing" philosophy is that if an object acts like something else, you should treat it like that and not check if it is the right class before attempting to act upon it. For example, if your function expects a list, but all it does is iterate over the elements in the list, it should just do the iteration without asserting that the variable contains a list. That way someone using your function can pass in a tuple, or an object that acts like a list, and it'll still work instead of breaking unnecessarily.Type
@AlexT Java on the other hand is far more strict about this, and you have to make sure the variable is something that supports iteration before attempting to iterate over it.Type
Not trying to bash your answer freely, but since it is the accepted one... Your example almost implies one would use issubclass(instance, Class) while it really is issubclass(Class, Superclass)Glace
Besides.. I dont find "duck typing" to be more elegant at all. 1 line check to see if it's a duck vs 3 lines of try/catch exception handling. I'll take the 1 line solution.. thanks!Garland
Given also the comments, @Nagoya didn't deserve so much upvotes, the answer, built collectively, did.Ibby
W
62

issubclass(class, classinfo)

Excerpt:

Return true if class is a subclass (direct, indirect or virtual) of classinfo.

Wolfort answered 6/2, 2011 at 11:30 Comment(0)
R
34

You can use isinstance if you have an instance, or issubclass if you have a class. Normally thought its a bad idea. Normally in Python you work out if an object is capable of something by attempting to do that thing to it.

Radiometeorograph answered 6/2, 2011 at 11:33 Comment(6)
What if you find out you can't do that thing with it? Do you catch an exception and try something else?Byrdie
@wrongusername: That is the 'Pythonic' way, yes. I think this custom has merit, so I follow it as long as it keeps my code clear. There's a good discussion about this here: #7605136Improvement
@Michael Scheper: I have to say, if that's the pythonic way, then I really hate the pythonic way. IMO, exceptions should NEVER be used for control flow. If you think an error might occur, protect against it... don't treat it like a GOTO. Interesting link you posted, thoughRayerayfield
@aboveyou00: Sounds like the kind of cases you're talking about violate 'as long as it keeps my code clear'. I'm not a fan of all Pythonic customs, though, since a lot of people abuse the EAFP principle and end up creating hard-to-find bugs.Improvement
This check comes handy when defining contracts. For instance, a constructor that receives an object: we would like to assert that the object received is an instance of a given class, and by doing so we inform the code reader what the constructor expects.Pintsize
What if I want to print out all subclasses of a superclass? Do I just attempt to print out every class and hope the reader skips over the erroneous lines? Oh wait, but I actually have to print out every object regardless if it's a class or not... What is the point of introspection if we aren't allowed to use it without a lecture on how abnormal it is?Hilaire
B
26

The issubclass(sub, sup) boolean function returns true if the given subclass sub is indeed a subclass of the superclass sup.

Buckeen answered 18/8, 2014 at 13:9 Comment(1)
The answer without a misguided lecture +1.Commandant
A
14

issubclass minimal runnable example

Here is a more complete example with some assertions:

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

Tested in Python 3.5.2.

Atmospherics answered 28/1, 2019 at 14:20 Comment(0)
H
7

According to the Python doc, we can also use class.__mro__ attribute or class.mro() method:

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
Hypochlorite answered 10/7, 2019 at 9:46 Comment(0)
R
2

@snakile, use this code:

#!/usr/bin/python3

class Suit: pass
class Heart(Suit): pass
class Spade(Suit): pass
class Diamond(Suit): pass
class Club(Suit): pass
class No(): pass

suit = Club()
f = issubclass(suit.__class__, Suit)
print(f)

suit = Spade()
f = issubclass(suit.__class__, Suit)
print(f)

suit = No()
f = issubclass(suit.__class__, Suit)
print(f)

Output:

$ /usr/bin/python3 sc.py
True
True
False
Rockett answered 20/2, 2023 at 10:43 Comment(0)
P
1

You can use the builtin issubclass. But type checking is usually seen as unneccessary because you can use duck-typing.

Philadelphia answered 6/2, 2011 at 11:34 Comment(0)
B
1

Using issubclass seemed like a clean way to write loglevels. It kinda feels odd using it... but it seems cleaner than other options.

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)
Betty answered 9/3, 2015 at 4:27 Comment(0)
S
-6
#issubclass(child,parent)

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

print(issubclass(c,b))#it returns true
Soble answered 13/9, 2016 at 13:16 Comment(1)
Code-only answers are not appreciated on SO, you should always add some explanatory text to the code. However, this answer is superfluous: it adds no information that hasn't already been mentioned in the previous answers.Alcorn

© 2022 - 2024 — McMap. All rights reserved.