Execute Program

Python in Detail: Isinstance and Type

Welcome to the Isinstance and Type lesson!

This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!

  • We've used isinstance to check that a value has a certain type.

  • >
    isinstance(2, str)
    Result:
    FalsePass Icon
  • We've also used the built-in type function to get type objects.

  • >
    type(True)
    Result:
  • >
    type(77)
    Result:
    intPass Icon
  • And we've seen that the int returned by type(...) is the same int that we pass to isinstance.

  • >
    int_type = type(66)
    isinstance(77, int_type)
    Result:
    TruePass Icon
  • So far, we've called int and str "type objects", but they're actually classes. The terms "type", "type object", and "class" are used interchangeably in Python, so we'll use them all in this course.

  • Every Python value is an instance of some class, even primitive values like strings and integers. For example, "Keanu" is an instance of the str class, and 77 is an instance of int.

  • When we convert a string into an integer with int("77"), we're really instantiating the int class with an argument of "77". When we print type(some_value), we're really printing some_value's class.

  • Classes support the equality operator, which works as we'd expect.

  • >
    type("three hundred") == int
    Result:
    FalsePass Icon
  • >
    type(300) == int
    Result:
    TruePass Icon
  • >
    type(300) == type("300")
    Result:
    FalsePass Icon
  • If we can simply compare the type of a value with any class, why do we need isinstance? The answer lies in subclassing.

  • In this example, Cat and Dog both inherit from the parent Animal class.

  • >
    class Animal:
    pass

    class Dog(Animal):
    def speak(self):
    return "woof"

    class Cat(Animal):
    def speak(self):
    return "meow"
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    luna = Dog()
    type(luna) == Dog
    Result:
    TruePass Icon
  • We know that Ms. Fluff is both a cat and an animal. But we can't use type and == to ask "is Ms. Fluff an animal?"

  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    ms_fluff = Cat()
    type(ms_fluff) == Cat
    Result:
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    ms_fluff = Cat()
    type(ms_fluff) == Animal
    Result:
    FalsePass Icon
  • To answer that question, we need isinstance. It tells us whether a value is an instance of the provided class, or its parent class, or any of its ancestor classes.

  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    ms_fluff = Cat()
    isinstance(ms_fluff, Cat)
    Result:
    TruePass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    ms_fluff = Cat()
    isinstance(ms_fluff, Animal)
    Result:
    TruePass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    woofy = Dog()
    isinstance(woofy, Animal)
    Result:
    TruePass Icon
  • It's common to use isinstance as a "guard" to make sure that we have an instance of the expected class. For example, animals make noises, but so do other objects. We can use an isinstance guard to make sure that we only add animals to a list of pets .

  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    class DialUpModem:
    def speak(self):
    return "beeep brrr bzzzz kchkchkch"

    pets = []

    def add_animal(animal):
    if not isinstance(animal, Animal):
    raise ValueError("only animals are allowed")
    pets.append(animal)

    add_animal(Dog())
    add_animal(Cat())
    add_animal(DialUpModem())
    Result:
    ValueError: only animals are allowedPass Icon
  • Many libraries add isinstance checks to help users diagnose mistakes. Others don't have these checks, and instead embrace Python's dynamic behavior. In some situations, we might not care about the class at all. We might only care that the object has the methods that we want to call.

  • Different teams follow different philosophies around using isinstance, so it's a good idea to learn and follow your team's existing conventions.

  • Here's a code problem:

    Only admin users should be on a list to receive security notifications. Right now, anyone can be added to the list. Add an isinstance check to ensure only Admins are allowed.

    class User():
    def __init__(self, name):
    self.name = name

    class Admin(User):
    def __init__(self, name):
    super().__init__(name)
    self.admin = True

    security_update_recipients = []
    def add_to_admin_list(users):
    for user in users:
    if isinstance(user, Admin):
    security_update_recipients.append(user.name)
    amir = User("Amir")
    betty = Admin("Betty")
    cindy = Admin("Cindy")
    dalili = User("Dalili")

    add_to_admin_list([amir, betty, cindy, dalili])
    security_update_recipients
    Goal:
    ['Betty', 'Cindy']
    Yours:
    ['Betty', 'Cindy']Pass Icon
  • Finally, if type(...) gives us an object's class, why isn't it called classof? As is often the case for questions like this, it comes from Python's history.

  • In earlier versions of Python, built-in values like integers and strings weren't instances of any class. They existed totally outside of Python's class system. Now every value is an instance of some class, but the type function still has its old name to maintain backward compatibility with older Python code.