So, you think you can’t use Types in Python?

So, you think you know about types in Python, well maybe you need to think again! Recent versions of Python, including 3.10 and October’s 3.11 have increased the support for type hints so that they are now quite usable.

12-05-2022
Bcorp Logo


So, you think you can’t use Types in Python?


So, you think you know about types in Python, well maybe you need to think again! Recent versions of Python, including 3.10 and October’s 3.11 have increased the support for type hints so that they are now quite usable. These can be used by analysis tools and IDEs to help developers create more stable and reliable applications.

Python's Type System

Many people consider Python to be an untyped programming language. However, that is not quite true. The type system in Python can be referred to as representing a Dynamically Typed programming language. That is, a variable holds a value and the type of that value is known and understood by the language. At runtime Python checks that what you are trying to do is valid given the types involved. For example, you can use the type() function to find out what type of thing a variable holds at any point in time:

a_variable = 42
print(f'a_variable type = {type(a_variable)} = {a_variable}')
a_variable = 1.345
print(f'a_variable type = {type(a_variable)} = {a_variable}')
a_variable = "Hello"
print(f'a_variable type = {type(a_variable)} = {a_variable}')
a_variable = True
print(f'a_variable type = {type(a_variable)} = {a_variable}')

This produces as output:

a_variable type = <class 'int'> = 42
a_variable type = <class 'float'> = 1.345
a_variable type = <class 'str'> = Hello
a_variable type = <class 'bool'> = True

Of course, as the above shows, a variable can hold different types of things at different types, hence the term dynamically typed.

As Python knows what types variables hold, it can check at runtime that your programs are valid / correct given the types involved in for example an operation. For example, it is valid to add to integers together and indeed two strings together (as this is string concatenation) but attempting to add an integer to a string will result in a TypeError:

print(1 + 1)
print(1.2 + 3.4)
print("Hello" + "world")
print("Hello" + 1)

This code produced the following output including the TypeError:

2
4.6

Helloworld
Traceback (most recent call last):
File "/Users/jeh/temp/pythonProjects/course/main.py", line 15, in <module>
print("Hello" + 1)
TypeError: can only concatenate str (not "int") to str

The Challenge for Python Developers

The challenge for Python Developers comes when they need to understand what types are required by, or work with, some API. As a very simple example, consider the following function:

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

What types can be used with this function? In essence any type can be used for the parameter x that supports the plus operator (+) with the type in y. From the above we know that integers and strings can be used, but we can also use floating point numbers, for example:

print(add(1, 2))
print(add(1.2, 3.4))
print(add(1, 3.4))
print(add(5.5, 1))
print(add("Hi", "There"))

The output from the above code is:

3
4.6
4.4
6.5
HiThere

Even custom types can be used if they implement the special __add__(self, other) operator method, for example:

class Quantity:
    def __init__(self, amount):
        self.amount = amount

    def __add__(self, other):
        return Quantity(self.amount + other.amount)

    def __str__(self):
        return f"Quantity({self.amount})"


q1 = Quantity(5)
q2 = Quantity(4)
print(add(q1, q2))

The __add__() method allows the custom type (class) being defined to be used with the add operator (‘+’). Thus this program generates the following output:

Quantity(9)

However, what was the intent of the designer of this add() function? What did they expect you to add together? The only option in traditional Python code is for the developer to provide some form of documentation, for example in the form of a docstring:

def add(x, y):
    """adds two integers together and 
       returns the resulting integer."""
    return x + y

Static Typing

Languages such as Java, C# and C are statically typed languages. That is when a variable, object attribute, parameter or return type is defined the type of that element is specified statically a compile time.

Thus, an add() method on a Java class Calculator might be written as follows:

package com.jjh;

public class Calculator {

  public int add(int x, int y) {
    return x + y;
  }

}

This makes it clear that the add() method will only handle integers and will return as a result an integer type. Thus, there is no possibility that a developer might try to add a number to a Boolean value etc.

The Java Calculator class can be used as shown below, note that this code will not even compile if the developer tries to add two Strings together. In this case we are adding two integers together, so all is fine:

package com.jjh;

public class App  {
  public static void main( String[] args ) {
    System.out.println( "Starting" );
    Calculator calc = new Calculator();
    System.out.println(calc.add(4, 5));
    System.out.println("Done");
  }
}

As the above program uses valid integer types with the add() method, the output from the compiled and executed program is:

Starting
9
Done

Python Type Hints

Python’s Type Hints are more like a half-way house between traditional Python’s lack of typing information at all and the static typing approach of languages such as Java. A Type Hint is additional type information that can be used with a function definition to indicate what types parameters should be and what type is returned. This is illustrated below:

def add(x: int, y: int) -> int:
    return x + y

In this case it makes it clear that both x and y should be ints (integer types) and the returned result will be an int. However, adding Type hints as shown above has no effect on the runtime execution of the program; they are only hints and are not enforced by Python per se. For example, it is still possible to pass a string into the add function as far as Python is concerned.

However, static analysis tools (such as MyPy) can be applied to the code to check for such misuse. Some editors, such as the widely used PyCharm, already have such tools integrated into their code checking behaviour. Thus attempting to pass a string into the add() function causes the following warning to be displayed by PyCharm:


So, you think you can’t use Types in Python?


If you want to use a tool such as mypy instead, or in addition to that available in your IDE, then you can install it using

pip install mypy

Or if you want to use conda / anaconda by using

conda install mypy

You can now analyse your code by applying mypy to a Python file, for example:

% mypy main.py
main.py:3: error: Incompatible types in assignment (expression has type "float", variable has type "int")
main.py:5: error: Incompatible types in assignment (expression has type "str", variable has type "int")
main.py:24: error: Argument 1 to "add" has incompatible type "str"; expected "int"
main.py:24: error: Argument 2 to "add" has incompatible type "str"; expected "int"
main.py:44: error: Argument 1 to "add" has incompatible type "Quantity"; expected "int"
main.py:44: error: Argument 2 to "add" has incompatible type "Quantity"; expected "int"
Found 6 errors in 1 file (checked 1 source file)

Type Hint Layout

The Python Style Guide defined by PEP 8 (Python Enhancement Proposal 8) provides some guidance for using Type Hints, for example:

  • Use normal rules for colons, that is, no space before and one space after a colon: text: str.
  • Use spaces around the = sign when combining an argument annotation with a default value: align: bool = True.
  • Use spaces around the -> arrow: def headline(…) -> str.

Type Hint for Multiple Types

Of course our add() function could work with floating point numbers as well as it works with integers. It would therefore be useful to be able to state this in terms of the type hints. Prior to Python 3.10 this could be done using a Union type, for example Union[int, float] which while it worked was a little unwieldy. Since Python 3.10 we can use the or style syntax bar ‘|’ for example int | float as shown below:

def add(x: int | float, y: int | float) -> int:
    return x + y

The Self Type

Python 3.11, which is due out in October 2022, will (probably) introduce the Self type Which is defined in PEP 673. This can be used to indicate that a method returns a reference to itself, for example:

from typing import Self

class Shape:
    def set_scale(self, scale: float) -> Self:
        self.scale = scale
        return self

The Benefits of Type Hints

There are a range of benefits to using Type Hints in Python, for example:

They help catch some errors within programs. Obviously, the biggest benefit is that type hints can help developers catch certain types of problems in their code (assuming that some form of type checker is used).

They provide Documentation. Type Hints can also act as a level of document that editors such as IDEs can pick up and display to other developers.

They can be work with IDEs. They can help with code generation and IDE auto-file functionality.

They can make developers stop and think. They can help ensure that developers think about their code and what types should be supported.

They can improve understanding of libraries. Although Type Hints may offer little advantage in a single use script, or throw away program, they can be of significant benefit when a library is being created. Such libraries will be used by a range of different developers and some may be released into the wild, for example via PyPI the Python Package Index. The use of type hints can greatly enhance others understanding of the APIs provided by these libraries.

Summary

If you are just starting out with Python, or you are writing scripts that will only be used once, then Type Hints may not be particularly useful. However, if you are creating libraries or developing larger more complex applications with teams of developers, then they can be very useful.


Would you like to know more?

If you found this article interesting you might be interested in some of our courses:

Share this post on:

We would love to hear from you

Get in touch

or call us on 020 3137 3920

Get in touch