Difference between isinstance and type in Python
Introduction
Python programming language is a dynamically typed language. In other words, variable types are not explicitly declared as in statically typed languages (ex. C++). Instead, type checking is performed at runtime as opposed to compile time. The debate of which is better? a dynamically typed or statically typed language is not discussed in this post.
In the example below, x is not declared as integer nor y is declared as string, however Python interpreter recognizes variable types from the assigned values.
1 2 3 4 |
# Python knows x is integer x = 1 # Python knows y is string y = 'Hello' |
Dynamic typing is convenient but it may cause some operations to fail due to incompatible variable types. For example, you cannot add an integer to a string. Consider the following example:
1 2 3 |
x = 1 y = 'Hello' z = x + y |
If we run the code snippet above, we will get a type error as follows:
1 |
TypeError: unsupported operand type(s) for +: 'int' and 'str' |
type() vs isinstance()
To prevent such errors by sanitizing input, Python provides builtin functions to perform type checking at runtime. In today’s code snippet, we are going to give examples for the following functions:
- type(object) : as the name indicates, it returns the type definition of the input object
- isinstance(object, class) : returns true if the object is an instance of the provided class or an instance of a derived class (i.e. sub class)
Let us take few examples…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# Define a shape base class class Shape(object): def __init__(self, n): self.name = n # Define a square class class Square(Shape): def __init__(self, w): self.width = w # Define a rectangle class class Rect(Shape): def __init__(self, w, h): self.width = w self.height = h # Instaniate some shapes sh = Shape('shape') sq = Square(1) sq.name = "square" rc = Rect(1, 2) rc.name = "rectangle" # This will print a False # because rc is of type Rect # and sh is of type Shape # Even though a Rect inherits # from Shape still they are # two different types print (type(rc) == type(sh)) print(type(sh)) print(type(sq)) print(type(rc)) # This will print True because # rc is an instance of Rect print(isinstance(rc, Rect)) # This will also print True because # rc is a subclass of Shape print(isinstance(rc, Shape)) # This will print False because # rc is not an instance of square print(isinstance(rc, Square)) |
If we run the code snippet above, we will get the following output:
1 2 3 4 5 6 7 |
False <class 'Shape'> <class 'Square'> <class 'Rect'> True True False |
Which function to use?
Both functions type() and isinstance() can be used to get the type of an object but It is recommended to use isinstace() as it supports inheritance in addition to type checking. On the other hand, type() is conveniently available if only type checking is needed. Since Python is dynamically typed language, a question arises: do we really need type checking in the first place? This brings us to duck typing…
Duck typing
If you are curious about the origin of the name? it comes from the phrase “If it looks like a duck and quacks like a duck, it’s a duck” but what does that mean ? The idea is simple, since Python is dynamically typed language (i.e. variable types are not declared) then there is no need to specify object types in order to know if an operation is valid or not. We just invoke the operation assuming it will work just fine if it is defined otherwise it will fail. We can always include duck typed code in a try catch block and handle type exceptions in case they occur. In other words, explicitly declaring object types is not needed before performing any operation on an object as it is only a matter of concern at runtime. Duck typing is nothing but a side effect of the language design, it is not intentional.
Here is an example:
1 2 3 4 5 6 |
def operation(x, y): return x + y print(operation(1, 2)) print(operation('Hello-', 'World')) print(operation([1, 2], [3, 4])) |
If we run the code snippet above, we will get the following output:
1 2 3 |
3 Hello-World [1, 2, 3, 4] |
As you can see, Python interpreter recognized the integers so addition was performed. Strings and lists were also recognized so concatenation was performed.
That is it for today. Let us summarize…
Summary |
---|
type(object) returns the type definition of the input object |
isinstance(object, class) returns true if the provided object is an instance of the provided class or a derived class |
Duck typing: explicitly declaring object types is not needed before performing any operation on an object |
References