Python - Type Hinting

A strong type system can not only prevent errors but also guide you and provide feedback in your design process.

Python - Type Hinting

Revisiting the Basics

While learning any new programming language, we mostly start from the datatypes and the features it provides related to data and its handling. You may have come across various statements like: "This particular programming language is strongly statically typed", etc. while exploring a new programming language. Before diving deep into our topic, let's explore some of the basic terminologies related to the data types in programming languages.

Data Type

Data type is a characteristic that computers use to identify and understand the nature and behavior of data. Data types define how data is stored, manipulated, and interpreted by the system.

Data types allow programming languages to implement constraints on data, by which the system can determine the memory it should allocate for the data, the operations that it can perform on that data, and the possible values for that data category and can prevent some unexpected errors, such as type errors or overflow errors, in the program.

Statically Typed Programming Languages

A language is statically typed if the data type of a variable is known at compile time.

Having the knowledge of datatypes at the compile time allows the system to catch unexpected errors beforehand, which makes the program easier to debug and understand. However, it requires the programmer to specify data types for all the variables which increases the development time. This approach focuses on performance rather than flexibility. Some examples of statically typed programming languages include Java, Rust, C, C++, etc.

Dynamically Typed Programming Languages

A language is dynamically typed if the data type of a variable is known at run time.

In dynamically typed programming languages, we are dependent on the interpreter or compiler to determine the datatype of a variable at run time or execution time (also known as type inference). This introduces flexibility as the programmer doesn't have to specify the data types of the variables and can solely focus on developing the business logic, leading to concise code. However, this decreases the performance as the system has to perform type inference during program execution. This approach focuses on flexibility rather than performance. Some examples of dynamically typed programming languages include Python, Ruby, JavaScript etc.

Static vs Dynamic Typing? Which is better?

It's normal to have this question about which set of programming languages is better, Statically typed or dynamically typed. The answer is "it's a trade-off".

Statically typed languages trade convenience and flexibility for safety and performance, while dynamically typed languages trade safety and performance for convenience and flexibility.

Static vs Dynamic Typing Trade-Offs

In a nutshell, neither approach is universally better; it's about selecting the approach that aligns with the goals and constraints of a particular project. Now that the basics are out of our way, let's talk about Type Hinting, Python's way of providing safety with convenience.

What is Type Hinting?

Type Hinting is an optional feature introduced in Python 3.5 that allows you to specify the expected data type of a variable, function parameter, or function's return value. It doesn't have any effect on the runtime behaviour of Python's source code.

Type Hints were added in Python as a way to standardize static typing in Python's source code.

Type hints are added using annotations, which are expressions that follow a colon after a variable name or a function parameter name, and an arrow after a function signature. For example:

Simple function to add two numbers in Python (without type hinting):

def add_two_nums(a, b):
   """Function to add two numbers"""
    return a + b

Simple function to add two numbers in Python (with type hinting):

def add_two_nums(a: float, b: float) -> float:
    """Function to add two numbers"""
    return a + b

Here, the annotation a: float indicates that the parameter a should be of type float, and the -> float annotation indicates that the function returns a float value.

Please, note that if we call our function with string parameters, it will provide a concatenated string as output. Type hinting does not enforce static typing but allows the developers and IDEs to detect potential errors or unexpected behaviours.

Why Type Hints?

To understand the need and benefits of Type Hints in Python, let's look at some of the examples.

IDE support and easy detection of potential errors:

Let's take our earlier created function add_two_nums and pass two strings (say "Hello" and "World") as parameters as given below:

def add_two_nums(a: float, b: float) -> float:
    """Function to add two numbers"""
    return a + b

if __name__ == "__main__":
    print(add_two_nums("Hello", "World"))

When we do this in our IDE (with the Type Hinting feature enabled), it will provide the below warning:

Argument of type "Literal['Hello']" cannot be assigned to parameter "a" of type "float" in function "add_two_nums"
"Literal['Hello']" is incompatible with "float"

This is a very useful feature while developing Python-based software as it will detect potential errors and unexpected results at the development stage itself, which will save a lot of time and effort. Utilizing the type hinting functionality of Python, several IDEs and Linters provide code completion, syntax highlighting, error detection, refactoring, and testing features, which can enhance your productivity and code quality.

More readable and documented code:

Just by seeing a Type Hinted code in Python, you can understand the expected inputs and outputs of a method or module. This will help a lot in maintaining consistency and correct usage of data structures across the whole codebase.

Type Hinting indirectly documents your codebase and makes it more understandable.

Type-Driven Development and Gradual Typing:

While using a dynamically typed language like Python, using features like Type Hinting provides a sense of safety despite all the flexibility and convenience the programming language provides. Python also allows us to implement Type Driven Development and Gradual Typing. Let's go through each of them:

  • Type-Driven Development: Strategy for designing and implementing programs guided by types. In this approach, developers first specify the types of inputs and outputs of all the parts of the program and then develop the business logic to meet these input and output types.

  • Gradual Typing: Strategy to leverage desirable aspects of both dynamic and static typing. This approach allows developers to incrementally annotate specific parts of the programs to enhance code clarity, catch errors early, and improve IDE/tools support.

Conclusion and Further Reading

In this article, we explored how Python utilizes the Type Hinting feature to counter some of the drawbacks of dynamically typed languages. In summary, we can summarize the Type Hinting feature using a quote that goes by:

In a dynamically typed world, type hinting is the gentle guide that keeps your codebase from wandering into the unknown.

Type Hinting is a vast topic, and if you wish to explore more, here are some of the reading suggestions on Type Hinting:

Leave a Like, if you find this helpful. Ciao !!!