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!
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:
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.
Step One: Fortifying Our Code with Integer Type Safety
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:
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:
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:
Now we can finish improving our code by Pattern Matching on the result:
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
I decided to delay the introduction of subscriptions, you can read the full story here.
In the meantime, I decided to accept donations.
If you can afford it, please consider donating:
Every donation helps me offset the running costs of the site and an unexpected tax bill. Any amount is greatly appreciated.
Also, if you are looking to buy some Swag, please visit I invite you to visit the TuringTacoTales Store on Redbubble.
Take a look, maybe you can find something you like: