Type checking is a process used in programming to verify that variables, expressions, or values in a program conform to specific data types or classes. It ensures that the data being operated on is of the expected type, which can help prevent certain types of errors and make code more robust and maintainable. Here’s why type checking is important and why we need it.
Why We Need Type Checking
It is a valuable practice in programming that enhances code quality, reduces errors, improves maintainability, and can save time and effort during development and debugging. While Python is dynamically typed, the use of type hints and type checking tools can bring some of the benefits of statically typed languages to the Python ecosystem
Type checking can be categorized into several different types based on when and how it is performed within the software development process. Here are the main types of type checking:
Static Type Checking:
Performed at compile-time or during static analysis, before the program is executed. It involves examining the code and its type annotations without running the program. Static type checking helps identify type-related errors and inconsistencies without actually executing the code. Languages like C, C++, Rust, Go are statically typed.
Example of Static type check on Go:
in this example, we have declared two variables,
y, with different data types:
float64. Go’s static type checking will catch type-related errors at compile-time:
x = 3.14 attempts to assign a
float64 value to an
int variable, which is a type mismatch. This will result in a compilation error like:
cannot use 3.14 (type float64) as type int in assignment
z := x + y tries to add an
int and a
float64, which is also a type mismatch. This will result in a compilation error like:
invalid operation: x + y (mismatched types int and float64)
Dynamic Type Checking:
Performed at runtime while the program is executing. It involves checking the types of variables and data as the program runs. Python is an example of a dynamically typed language where type checking occurs at runtime. Dynamic type checking can catch type errors that may not be detected by static analysis.
In Python, variables do not have fixed types, and the type of a variable can change during the execution of a program. Here’s an example of dynamic type checking in Python:
In this Python example:
y are assigned different types, an integer and a string, respectively. Python allows dynamic type assignment.
+ operator is used to concatenate the strings
y, demonstrating that Python checks types during runtime.
Attempting to add a string and an integer in the line
z = x + 5 results in
a TypeError at runtime, as the types are incompatible.
You can use the
type() function to check the type of a variable at runtime. The
isinstance() function checks if a variable is an instance of a specified class or type.
Dynamic typing in Python offers flexibility and simplicity but can lead to type-related runtime errors if you’re not careful. It’s essential to write code that accounts for potential type mismatches and exceptions that may arise during runtime.
Enforces strict type rules, and it doesn’t perform implicit type conversions. In a strongly typed language, operations are only allowed on data of compatible types. If types are not compatible, the code will generate an error. Python is an example of a strongly typed language.
In this example, we are trying to concatenate a
string and an
integer in Python, which enforces strong typing. This operation results in a TypeError because Python doesn’t implicitly perform the conversion between string and integer types. To resolve the error, we explicitly convert the
integer to a
The key point is that strong typing requires you to be explicit about type conversions and operations, making the code more predictable and reducing the likelihood of unintended behavior or errors.
string and a
string that contains a number by a
"5" to a number and performs the multiplication, resulting in the value
There are a number of tools out there that use type hints for static and runtime type checking.
Runtime type checking / data validation
Static Type Checking:
- Static type checking refers to the process of verifying the correctness of types in a program at compile time, before the program is executed.
- It is typically associated with statically typed programming languages, where the type of a variable or expression is known and checked at compile time.
- Errors and type inconsistencies are detected during compilation, and the program will not be executed until these errors are resolved.
- Static type checking helps catch type-related errors early in the development process, reducing the risk of runtime type errors.
Runtime Type Checking:
- Runtime type checking refers to the process of verifying the correctness of types in a program while the program is running.
- It is more common in dynamically typed programming languages, where types are associated with values at runtime, and the type checks occur during program execution.
- Errors related to type inconsistencies might only be discovered when the problematic code path is executed during runtime.
- This can lead to type-related runtime errors, such as “TypeError” in Python, and can make debugging more challenging.
Type hinting is a feature introduced in Python 3.5. It allows you to provide optional type annotations in your code using the typing module, without affecting the runtime behavior of your program.
Type hints are a way to document the expected types of variables, function parameters, and return values. They make your code more readable and can be used by tools like Mypy for static type checking.
Type hints do not enforce strict type checking at runtime. Python remains a dynamically typed language, so you can still assign values of different types to variables, and the code will run. Type hints are for documentation and tooling purposes.
Example of type hinting:
def add(a: int, b: int) -> int: return a + b
Why Use Type Checking and Hinting?
Using type checking and hinting in your Python code offers several significant advantages:
- Error Prevention: Type checking helps catch type-related errors at an early stage. This prevents runtime errors that can be difficult to debug and can lead to unexpected behavior.
- Code Readability: Type hints make your code more readable and self-documenting. They provide clarity about the expected types of variables, function parameters, and return values, making it easier for other developers to understand and maintain your code.
- Tooling Support: Type hints are not just for human readers; they can be used by static analyzers and linters like Mypy to catch type-related issues. These tools provide valuable feedback and can save you time by identifying potential problems before you run your code.
- Enhanced Collaboration: When working in a team, type hints make it easier for team members to collaborate on a codebase. Everyone can understand the types and expectations, reducing confusion and potential errors.
- Documentation: Type hints act as a form of documentation. They describe the intent of variables and functions, making it clear how they should be used.
At SCSS Company, we faced a specific requirement within one of our projects – the need for a robust currency conversion system. To address this need, we developed a Python-based solution, and the following code is a crucial component of this solution. The solution consists of two main classes: CurrencyConverter and GenerateCurrency. To ensure the code’s clarity and maintainability, we implemented extensive type hinting and checking, clearly specifying the expected data types for method parameters, return values, and class attributes. This not only enhances the code’s readability but also empowers us to catch type-related errors early in the development process. Let’s dive into a detailed breakdown of the code.
In this code:
We import the necessary modules, including the ABC class from the abc module, dataclass from the dataclasses module, and the requests library for making HTTP requests.
CurrencyConverteris defined as an abstract base class (ABC) with an abstract method
convertmethod now includes type hints: it takes a value parameter of type
floatand returns a dictionary (-> dict).
GenerateCurrencyis decorated with @dataclass, which automatically generates special methods like init, repr, and eq based on class attributes. Class attributes are defined with type hints and default values, specifying the expected types for each attribute.
GenerateCurrency, type hints are used throughout:
querystringis defined as a dictionary of type
requests.requestfunction is called with type hints for its arguments:
urlis of type
headersis of type
- The function returns a response object of type requests.Response.
The comprehensive use of type hinting and checking in this code makes it clear what types of values are expected for method arguments and return values. This not only improves code readability but also helps catch type-related errors early in the development process, making the code more robust and maintainable.
Well, it is an additional layer that protects us from unexpected bugs. plays a pivotal role in enhancing Python code quality and maintainability. By adding type hints to variables, function parameters, and return values, developers improve code readability and documentation, making it easier for teams to understand and maintain code. Type hints are not enforced by interpreter (i.e ignored by interpreter), but helps to express the intent of a variable, function, or class. Because of that it is suggested to use runtime checker to raise a error.