Type Hinting Improvements
00:10 Several releases ago, Python introduced the ability to annotate your code with metadata. At the time, the focus was on the annotations more than anything else. Very quickly, the most common use of annotations was to provide type hinting to the language.
00:24 Python is a dynamically typed language, which means there is very little that the compiler can do to detect the incorrect use of types. Python doesn’t let you add a string and an integer, but it has no way of knowing you’re attempting to do that until it actually executes the line of code.
00:40 Type hints are a way of introducing static typing style features into the otherwise dynamic language. You annotate your code with information about the allowed types for a given situation, which means tools can determine what type a function’s arguments expect, what type a function returns, or what type a variable contains.
There are situations where you want to support multiple types. A common example of this is a function that can take any number, whether int or float, as an argument. To specify a choice of types, Python has the
Union type that allows the joining of several different types.
The problem with this is it can get unwieldy and long. The
sum_list() function here in the example takes a list of numbers. The resulting type hint requires two special imports and takes up a lot of space. Python 3.10 introduces a shortcut for the
Union type, the pipe or
OR operator. Combining this with the Python 3.9 addition of being able to use the list itself as a type, and you no longer need the import and the type hint itself is shorter and easier to read.
The same operator can also be used to specify that a value is optional. This code shows a type hint for a variable named
address that can be a string or
None. Prior to Python 3.10, you would have had to import and use the
Optional type hint class to do the same thing.
The example here declares a
Card to be a tuple made up of two strings and a
Deck as a list of cards. This could be done prior to Python 3.10, but it was done in a way where there was ambiguity for the parser.
This is called a type guard. A clever static type checker now knows that past this point, the
suit has to be a string. So, the first part of the tuple in the return type of this function is always a string. Without the type guard, this function would fail its type check.
There’s a limited list of the kinds of code that a type checker can use as a type guard. Python 3.10 introduces the
TypeGuard class, allowing you to declare that a function should act as a type guard.
Now, when used inside of
get_score(), your type checker will treat this function as a type guard and modify its checking algorithm appropriately. Function decorators are closures that wrap a function. They’re useful for applying pre- and post-conditions when the wrapped function is called.
04:51 Because decorators are functions that wrap functions, they hide the type hint information of the thing they are wrapping. This makes type hinting a decorator limiting. In the code here, the callable class indicates that the decorator is wrapping a function.
This is a special class for type hinting that acts as a pass-through. The example code still indicates that the decorator takes a
Callable, but this time, the function being wrapped is hinted using the
get_annotations() will return all the annotation information associated with the argument. In this example, you see the type hint information associated with the arithmetic
06:24 This can make proper type hinting of a recursive class difficult. Python 3.7 introduced the idea of postponed evaluation of annotations, meaning you could annotate with names that hadn’t been declared yet.
The decision to implement postponed evaluation was itself postponed until Python 3.11 to give the library maintainers time to catch up. In the meantime, if you want to take advantage of this feature, you can import it from
Become a Member to join the conversation.