3

Generally (i.e. I haven't found any counter examples) in python isinstance() is preferred over type() because of checking inheritance, speed and/or seemingly neater look. Let's investigate a provocative example. Knowing that bool is a subclass of int, let's say that we want to check if an object is an instance of a class, but not its subclasses. The motivation behind doing so is that while checking/using subclasses might not be a problem in python (maybe?), it may cause problems when python interacts with external tools (for example in other languages bool is not necessarily a subclass of int). I see two options of performing such check:

if isinstance(var, int) and not isinstance(var, bool): ...

or

if type(var) is int: ...

and personally it looks like using type is a neater approach here. Also in case in the future we subclass int even more, type check would be easier to maintain.

The question is if this might be the case of using type over isinstance? Going one step even further: should we use type over isinstance in the case when we want to check if an object is of a particular class not including its subclasses. Any concerns?

PS If anyone's interested, the speed of both validations are almost identical with validation by type usually being a little faster. With the code:

import time


class Timer:

    def __init__(self):
        self.start_time = None

    def __enter__(self):
        self.start_time = time.time()

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(f"Total time taken: {round(time.time() - self.start_time, 3)}")
        self.start_time = None


def validate_isinstance(var):
    return isinstance(var, int) and not isinstance(var, bool)


def validate_type(var):
    return type(var) is int


for validation_method in (validate_isinstance, validate_type):
    with Timer():
        for _ in range(10**6):
            validate_isinstance(True)

I get:

Total time taken: 0.236
Total time taken: 0.226
Timus
  • 10,974
  • 5
  • 14
  • 28
Aidas
  • 114
  • 4
  • 1
    Well efforted question but I feel like this is already answered [here](https://stackoverflow.com/a/152596/3936044), comparing speed seems unnecessary as they do different things – Mandera Aug 22 '22 at 07:46
  • 1
    Checking `isinstance` instead of `type` is more in line with object-oriented paradigm. You should (at least usually) not care what the class of the object is, as long as it satisfies the Liskov substitution principle, which all subclasses are expected to. – zvone Aug 22 '22 at 08:21
  • @Mandera Thank You for the link to the other question - I wasn't aware of it. Despite it describing the behavior of both `type` and `isinstance`, I didn't find much motivation for using one over another. In the internet I see a huge trend using `isinstance` over `type` (with which I do agree). However panacea rarely being the case, I'm interested in the proc and cons of each. I even couldn't find the arguments in the PEP recommendations itself (link in the post), which is a bit strange. – Aidas Aug 22 '22 at 08:35
  • @zvone Let's say LSP is satisfied within my program. This still may not prevent from having bugs when communicating with some external API, where `bool` is not a subclass of `int` (python will allow me having `True` as `int` whereas external software may error out). I noted it in my question. – Aidas Aug 22 '22 at 08:39
  • @Aidas In Python, a boolean is an integer, i.e. any function which can handle a `value=1`, should be able to handle a `value=True` the same way. If your function is _"communicating with some external API"_, as you wrote, then it should format the input to that API as the API expects it. You could pass e.g. `int(value)` of f"{value:d}" to the external API. Having an `assert type(value) is int` means that your function cannot really handle all integers. BTW, I would agree that `bool` being subtype of `int` feels a bit unpythonic, which is why this is a bit illogical, but that is how it is... – zvone Aug 22 '22 at 09:01
  • @zvone Let's consider a simple example: I'm using python yaml lib to write a value, let's say `value: True`. If python reads this, then it can perform an operation `value + 1`. However, if we read the yaml by some other language and try to perform same `value + 1` operation, it may/will complain. So the python code can handle all integers and satisfy LSP, but the actual program may fail. And hence I disagree that `assert type(value) is int` means that function can't handle all ints. Furthermore, considering `int(value)` as an option: it'd been strange if python had required such interfaces... – Aidas Aug 22 '22 at 13:47
  • @zvone In other words, I think the most straightforward approach is to limit the type to int only instead of dealing with API. – Aidas Aug 22 '22 at 13:50
  • @Aidas Yes, there are exceptions to what I wrote. Your example is an exception and in that case, it makes sense to check `type(value) is int`. BTW, I would not use `assert` for that, because asserts can be disabled and should in fact be used only when you don't expect them to ever fail ;) – zvone Aug 24 '22 at 07:48
  • @zvone Thank You for the input! Also adding a [related post](https://stackoverflow.com/questions/64447317/check-if-an-item-is-an-instance-but-not-a-subclass). – Aidas Aug 24 '22 at 09:30

0 Answers0