Get up to 80 % extra points for free! More info:

Lesson 15 - Type System and Type Hints in Python

In the last lesson, Datetime Library for Python, we explained the date and time.

Despite that Python is dynamically typed, it's still a strongly typed language that requires assignment of data types. Although this usually happens automatically, there are still cases where it's appropriate to specify types in Python manually and explicitly. For example, by specifying types, our IDE can check the accuracy of our code better. This practice is very useful for larger projects.

Python itself ignores the types we specify and always assigns them itself, no matter what we explicitly state. This statement really only serves as information for us, for the IDE or mypy tool that validates types.

Basic Types in Python and Their Use

The basic types in Python are:

  • int for an integer
  • float for a real number
  • bool for True/False values
  • str for strings
  • None for None

If we want to explicitly declare a variable type, we use the colon operator ::

text: str = "Hello world!"

It's now clearly stated that text is of type str. Here it would be equally recognizable by the value of "Hello world!", so this specification is a little unnecessary. But there are a lot of places in the program where it's no longer so clear. E.g. we can also type parameters of functions and their return values:

def generate_hello(name: str) -> str:
    return "Hello, " + name + "!"

With the already mentioned tools, it's then possible to check if everything's OK, or to generate documentation for the functions, including types.

Other Types in Python

Of course, these are not all the types that can appear in Python. Surely you can name many more - list, dict, etc. A variable of type list can be marked with:

my_list: list = [1, 2, 3]

But this wouldn't be very useful to us. Although we'd know that the given variable is a list, we wouldn't know of what type the elements in this list are.

The typing Library

This is exactly what the typing library will help us with:

from typing import List, Set, Dict, Optional, Callable

Typing Lists and Sets

The first two imported items are certainly intuitive, the type of elements stored in them is determined by brackets:

my_list: List[int] = [1, 2, 3]
my_set: Set[int] = set(my_list)

Dictionaries

If we want to type a dictionary, one such parameter is not enough. So we'll need two parameters - the first for the key type and the second for the value type:

my_dictionary: Dict[int, str] = {1: 'one', 2: 'two', 3: 'three'}

Of course, we can use more complex types as parameters than just the primitive ones:

my_dictionary2: Dict[int, List[str]] = {1: ['one', 'uno'], 2: ['two', 'dos'], 3: ['three', 'tres']}

Optional

But when can the Optional type come in handy? Optional in short means that a given variable can take values of the type of the first parameter or None. This is useful, for example, if we want to create a division function and want to prevent division by zero:

def division(a: float, b: float) -> Optional[float]:
    if b == 0:
        return None  # cannot divide by zero
    return a / b

Callable

The last type we imported is called Callable. As the name suggests, this is a type of something that can be called - a function:

def add_one(a: int) -> int:
    return a + 1

operation: Callable = add_one
print(operation(5))  # prints 6

Creating Custom Types

There are three ways to create Python types:

  1. type aliases
  2. classes
  3. named dictionary

Type Aliases

If we use some complicated type often, we can create a type alias, which is created by simply assigning the original type to a variable. For example, this can be useful when defining matrices:

my_matrix: List[List[int]] = [[1, 2], [3, 4]]

can be written, thanks to creating a type alias, as:

Matrix = List[List[int]]
my_matrix: Matrix = [[1, 2], [3, 4]]

Classes

Each class is also considered its own type, whether we import it or create it ourselves:

class MyClass:
    pass

my_variable: MyClass = MyClass()

from queue import Queue

my_queue: Queue = Queue()

The problem we might encounter would be that we'd like to use a type that's declared later in the file (or the type of the given class itself). Therefore, the parser doesn't know it at the time of reading the given line. We can prevent this by enclosing its name in quotation marks as a string instead of a direct reference to the class. E.g.:

from typing import Optinal, Any

class LinkedList:
    def __init__(self):
        self.value: Optional[Any] = Noneelements
        self.next: Optional["LinkedList"] = None

Typed Dictionary

The last kind of types we can create in Python that we'll discuss today is the creation of a typed dictionary (TypedDict), which first appeared in Python 3.8. This is a kind of abstraction over the classic dictionary, but we'll explicitly state which keys with what type of values can be found in the dictionary:

from typing import TypedDict

class HouseAddress(TypedDict):
    city: str
    street: str
    house_number: int

HouseAddress = TypedDict('HouseAddress', city=str, street=str, house_number=int)
HouseAddress = TypedDict('HouseAddress', {'city': str, 'street': str, 'house_number': int})

my_address: HouseAddress = {'city': 'Nottingham', 'street': 'Meadway', 'house_number': 52}

Alternative Typing System

This system, which we have shown, has been available in Python since version 3.5, so if you're using Python 3.4 and older, we don't have anything from the typing library or the colon operator available. Nevertheless, we can still type - using comments. So, for example, we'd declare a string variable as follows:

my_text = "Hello"  # type: str

This system can also be used in Python 3.5 and later, and in some specific situations it is still the only possible way of typing without the interpreter reporting a piece of code that it cannot parse.

Further Studying

In this lesson, we've shown how to use basic typing in Python, which is enough for the vast majority of common cases. Recent versions of Python have proven that typing is something the language wants to deal with more and more, and is still evolving. If we'd like to find the most up-to-date typing possibilities, it's best to consult the official documentation.


 

Previous article
Datetime Library for Python
All articles in this section
Object-Oriented Programming in Python
Article has been written for you by Shift
Avatar
User rating:
No one has rated this quite yet, be the first one!
As an author I'm interested mainly in industry automation. I'm also writing music and literature. In my spare time I'm working on a few games in Game Maker Studio.
Activities