Type Hints: Python’s Weapon Against Code Anarchy

Unlock the potential of Python with type hints and pattern matching. Learn to enhance code readability and prevent errors in our latest article. Discover how to handle various numeric types effectively. Subscribe for more Python programming insights and join our community of coding enthusiasts!

Type Hints: Python’s Weapon Against Code Anarchy
The SWAT Team enforces Type Safety.

Overview

Imagine stumbling upon a Facebook quiz challenging you to guess the output of a Python snippet. The code looks simple enough, but there's a catch. It's deceptively complex and riddled with potential issues that take time to be spotted:

Python 3.12

def multiply(a, b):
	return a * b

def wrapper(func):
	def inner(a, b):
		if a == 0 or b == 0:
			print("can not multiply by zero")
		else:
			return a * b

	return inner

multiply = wrapper(multiply)
result = multiply(5, 0)

print(result)
        

The result will be None if one of the inputs is zero. Otherwise, it will be a Number. The exact behavior of code using such a function is difficult to predict, but chances are that it would end up in a TypeError, possibly far away from the code that caused it.

At "The Turing Taco Tales", we think the real question we should be asking isn't about the output but why such code is allowed to run in the first place. In Python, we often pride ourselves on the language’s readability and elegance, but without proper safeguards, even simple code can lead to unexpected behaviors.

Enter Type Hints and mypy, Python’s dynamic duo against code anarchy. Type hints allow us to explicitly define the types of variables, making the code more readable and maintainable. With the help of mypy, a static type checker, we can catch errors and problematic patterns before the code even runs.

The 'Type Police' in a neon-lit code city, under a Python logo, symbolizing disciplined type hint usage in programming.
The Python Type Police

Step One: Fortifying Our Code with Integer Type Safety

Python 3.12

from typing import Callable

def multiply(a: int, b: int) -> int:
    return a * b

def wrapper(func: Callable[[int, int], int]) -> Callable[[int, int], int]:
    def inner(a: int, b: int) -> int:
        if a == 0 or b == 0:
            print("cannot multiply by zero")
            return None  # Returning 0 instead of None
        else:
            return func(a, b)

    return inner

multiply = wrapper(multiply)
result = multiply(5, 0)

print(result)
        

By adding type hints to our Python code, we've made significant strides in enhancing its clarity and reliability. Type hints explicitly define the types of variables and function return values, bringing an added layer of documentation and error prevention to our codebase. In the case of our multiply function and its wrapper, type hints communicate that both the parameters and the return type are integers.

This explicitness not only aids in understanding the function's purpose and usage but also enables tools like mypy to perform static type checking, catching potential bugs before the code is even run:

Screenshot showing `mypy` detecting errors in the code that should be illegal
Screenshot showing mypy detecting errors in the code that should be illegal

Legalizing Our Code

The error in line 16th, of the example in the previous section was warning us about reusing names. This is why we renamed the variable to avoid name collisions:

Python 3.12

from typing import Callable, Optional

def multiply(a: int, b: int) -> int:
    return a * b

def wrapper(func: Callable[[int, int], int]) -> Callable[[int, int], Optional[int]]:
    def inner(a: int, b: int) -> Optional[int]:
        if a == 0 or b == 0:
            print("cannot multiply by zero")
            return None  # Returning 0 instead of None
        else:
            return func(a, b)

    return inner

multiplier = wrapper(multiply)
result = multiplier(5, 0) + 1

print(result)
        

Besides, to make the code type correctly, we used Optional; this will now warn us when using the result without dealing with the possibility of it being None:

`mypy` warning us about handling the undefined case
`mypy` warning us about handling the undefined case

Now we can finish improving our code by Pattern Matching on the result:

Python 3.12

from typing import Callable, Optional

def multiply(a: int, b: int) -> int:
    return a * b

def wrapper(func: Callable[[int, int], int]) -> Callable[[int, int], Optional[int]]:
    def inner(a: int, b: int) -> Optional[int]:
        if a == 0 or b == 0:
            print("cannot multiply by zero")
            return None  # Returning 0 instead of None
        else:
            return func(a, b)

    return inner

multiplier = wrapper(multiply)

match multiplier(5, 0):
    case None:
        print("Multiplication by zero isn't allowed")
    case int(result):
        print(f"Result of operation: {result + 1}")
        

Conclusion

In this exploration, we've seen how type hints and structural pattern matching can significantly improve the clarity, safety, and reliability of Python code. The final version of this sample script is in our GitHub repository.

Please check our next article, where we'll take this concept further by making our function work seamlessly with any numeric type, not just integers.

Please consider subscribing to our content if you've found value in this article. We sincerely appreciate your support, as it fuels our continued efforts to demystify Python programming and share valuable insights.


Addendum: A Special Note for Our Readers

Before we sign off, we’d like to share something special with our readers who’ve appreciated the visual journey accompanying our articles.

We invite you to visit the TuringTacoTales Store on Redbubble. Here, you can explore a range of products featuring these unique and inspiring designs.

Each item showcases the beauty and creativity inherent in coding. It’s a perfect way to express your love for programming while enjoying high-quality, artistically crafted merchandise:

So, please take a moment to browse our collection and perhaps find something that resonates with your passion for Python and programming!