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
isinstanceto check that a value has a certain type.>
isinstance(2, str)Result:
False
We've also used the built-in
typefunction to get type objects.>
type(True)Result:
>
type(77)Result:
int
And we've seen that the
intreturned bytype(...)is the sameintthat we pass toisinstance.>
int_type = type(66)isinstance(77, int_type)Result:
True
So far, we've called
intandstr"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 thestrclass, and77is an instance ofint.When we convert a string into an integer with
int("77"), we're really instantiating theintclass with an argument of"77". When we printtype(some_value), we're really printingsome_value's class.Classes support the equality operator, which works as we'd expect.
>
type("three hundred") == intResult:
False
>
type(300) == intResult:
True
>
type(300) == type("300")Result:
False
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,
CatandDogboth inherit from the parentAnimalclass.>
class Animal:passclass 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) == DogResult:
True
We know that Ms. Fluff is both a cat and an animal. But we can't use
typeand==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) == CatResult:
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
ms_fluff = Cat()type(ms_fluff) == AnimalResult:
False
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:
True
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
ms_fluff = Cat()isinstance(ms_fluff, Animal)Result:
True
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
woofy = Dog()isinstance(woofy, Animal)Result:
True
It's common to use
isinstanceas 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 anisinstanceguard to make sure that we only add animals to a list ofpets.- 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 allowed
Many libraries add
isinstancechecks 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
isinstancecheck to ensure onlyAdmins are allowed.class User():def __init__(self, name):self.name = nameclass Admin(User):def __init__(self, name):super().__init__(name)self.admin = Truesecurity_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']
Finally, if
type(...)gives us an object's class, why isn't it calledclassof? 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
typefunction still has its old name to maintain backward compatibility with older Python code.