Python Type Checking

Filed Under: Python
Python Type Checking

1. What is Type Checking?

Type Checking is the programming language feature that specifies how the variables are created and their types are identified by the language compiler or interpreter.

2. What are the different types of Type Checking?

A programming language can be classified into the following based on type checking.

  1. Statically Typed Languages – C, Java, C++, etc.
  2. Dynamically Typed Languages – JavaScript, Python, Ruby, etc.

2.1) What is Static Type Checking?

The type of a variable is known at the compile-time. The type of a variable is fixed and we can’t change it at a later point of time.

Let’s look at the variable declaration in Java.


String str = "Hello";

If we try to change the type of the variable or assign a value of incompatible type, the compiler will throw an error.


str = 10; // Type mismatch: cannot convert from int to String 

int str = 10; // Duplicate local variable str

2.2) What is Dynamic Type Checking?

The type of the variable is determined at the runtime. We don’t specify the type of the variable in the code. The code behaves differently based on the type of the object at runtime.

Let’s look at an example of a function definition in Python.


def add(x, y):
    return x + y


print(add(10, 5))
print(add('A', 'B'))

If the function arguments are integers, the sum is returned. If they are string, they are concatenated and returned.

If we pass any other custom object, we might get a different response or an error is raised if the ‘+’ operator is not supported by them.

2.3) Statically Typed Languages vs Dynamically Typed Languages

  1. The benefit of statically typed language is that many errors related to incompatible types are caught at the compile-time. The same is not true with the dynamically typed languages. It might take a long time before you get the error related to the incompatible type.
  2. The benefit of dynamically typed language is the shorter development time. But, this benefit fades away when the project code size grows. It’s really hard to debug a program throwing error because of the wrong type as it will occur only once in a while based on the user input or the data received from another source.

3. Duck Typing in Python

Duck typing is a concept of dynamically typed programming languages. The type of object is less important than the functions it defines.

Let’s look at this with an example of a custom object and the add() function we have defined.


def add(x, y):
    return x + y

class Data:

    def __init__(self, i):
        self.id = i

d1 = Data(10)
d2 = Data(5)

print(add(d1, d2))

This code will produce the following runtime error:


Traceback (most recent call last):
  File "/Users/pankaj/Documents/PycharmProjects/hello-world/journaldev/type_checking.py", line 12, in <module>
    print(add(d1, d2))
  File "/Users/pankaj/Documents/PycharmProjects/hello-world/journaldev/type_checking.py", line 2, in add
    return x + y
TypeError: unsupported operand type(s) for +: 'Data' and 'Data'

If we want our object to support the addition operator, all we have to do is define __add__() function for it.


def __add__(self, other):
    return self.id + other.id

Now, the print statement will print 15 and there will be no error produced by the code.

So, in essence, the type of the object doesn’t matter at all. As long as the required functions are defined to support an operation, there won’t be any issues because of the object type.

4. Type Hints in Python

Python 3.5 added type hints support using the typing module. As the name suggests, it’s a way for the developers to hint the expected type of the function arguments and return types.

Let’s say we have a function to perform some operations on two numbers.


def calculate(x, y, op='sum'):
    if op == 'divide':
        return x // y
    if op == 'difference':
        return x - y
    if op == 'multiply':
        return x * y
    # default is sum
    return x + y

Even though it’s intended for numbers only, it will work for string arguments too.


print(calculate(10, 3, 'divide'))  # 3
print(calculate(10, 5))  # 15
print(calculate('A', 'B'))  # AB

Let’s see how to add type hints for the given function.


def calculate1(x: int, y: int, op: str = 'sum') -> int:
    # same code as above

The function argument type hints are provided with a colon (:) and the return type using -> sign.

But, this doesn’t enforce the function argument and return types. The code will still work for other types of arguments.

But, third party tools such as type checkers, IDEs, linters, etc. can parse this to warn us for the possibility of the wrong types of arguments. For example, if we pass string arguments to this function, the PyCharm IDE will produce a warning message as “Expected type ‘int’, got ‘str’ instead“.

4.1) Advantages of Type Hints

  • The type hints also documents the code for the function expected argument types and the return type.
  • It helps API users in making sure that the correct type of arguments are being passed to a function.
  • Helps improving IDE, type checkers, and Linters in warning users when an incompatible type of argument is being passed to a function.

4.2) Disadvantages of Type Hints

  • There is no runtime benefit for type hints. It doesn’t enforce the types or raises any warning or errors if a different type of argument is passed.
  • The type hints are more like documentation. If the function is changed and the developer misses to change the type hints accordingly, it will give a wrong message to the developer using the function. Maintaining type hints are time-consuming and requires developers’ effort.
  • It slows down the program speed a little bit.
  • The type hints were introduced in Python 3.5, so it’s fairly new and won’t work with older Python versions.

4.3) __annotations__ attribute

Python functions have __annotations__ attribute, which contains the information about the type hints.


def calculate(x, y, op='sum'):
    pass

def calculate1(x: int, y: int, op: str = 'sum') -> int:
    pass

print(calculate.__annotations__)  # {}

print(calculate1.__annotations__) 
# {'x': <class 'int'>, 'y': <class 'int'>, 'op': <class 'str'>, 'return': <class 'int'>}

5. Python Runtime Type Checking

We can use type() function to get the type of a variable at runtime.


>>> x = 10
>>> type(x)
<class 'int'>
>>> 
>>> s1 = 'Hello'
>>> type(s1)
<class 'str'>
>>> 

We can also use isinstance() function to check if a variable is of a certain type or not. This function returns a boolean value and accepts two arguments.


>>> x = 10
>>> isinstance(x, int)
True
>>> isinstance(x, str)
False
>>>
>>> o = object()
>>> isinstance(o, (int, str, object))
True

6. Python Static Type Checking

Python is a dynamically typed language. But, we can use mypy module for static type checking. Note that this will work only when we have added type hints to a function.

The mypy module will check the code and raise errors if we are calling the function with incompatible data type arguments. We can install mypy module using the PIP command.


pip install mypy

Let’s say we have a Python script type_checking.py with the below content.


def calculate(x, y, op='sum'):
    pass


def calculate1(x: int, y: int, op: str = 'sum') -> int:
    pass


calculate('a', 'b')
calculate1('a', 'b')

Now, we can run mypy from the command line to test this file for function argument types.


$ mypy type_checking.py
type_checking.py:10: error: Argument 1 to "calculate1" has incompatible type "str"; expected "int"
type_checking.py:10: error: Argument 2 to "calculate1" has incompatible type "str"; expected "int"
Found 2 errors in 1 file (checked 1 source file)
$

7. Conclusion

In this tutorial, we learned about the statically-typed and dynamically-typed languages. We also learned that for the large codebase, statically-typed code is more beneficial. We learned about the type hints in Python and how to use mypy module as a static type checker.

8. References

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages