Typing Features
00:00
In the previous lesson, I showed you exception groups, the new except*
syntax, and the TaskGroup()
addition to asyncio
.
00:08
In this lesson, I’ll be describing the additions to the typing
module.
00:13
The Self
type is used in a similar fashion to self
in a class. It indicates the use of the object being used. This is useful in factory methods, those methods on a class that return an instance of the object.
00:29
In this example, the .from_json()
method returns an instance of the Person
class. It’s a factory for the person based on a JSON string.
00:39
The Self
type indicates that the method is returning an instance of the class that the method belongs to.
00:48
The TypeVar
type is used to indicate the use of a single type, but what if you need more than one of those? Enter the new TypeVarTuple
.
00:58 This declares an arbitrary-length number of types. Generics like this are called variadic, meaning they are of variable length.
01:07 Consider this code that takes the first item in a tuple and moves it to the last item in a tuple. The function takes a tuple that starts with one thing, then is followed by a variable number of things.
01:21 The function returns a new tuple comprised of the variable-length things, and then the first thing.
01:30
Many libraries use dataclass-like semantics—for example, Django ORM
objects, Django REST framework serializers, Pydantic models, and Ninja schemas.
01:41 Each of these declares a grouping of fields which are very dataclass-like. If dataclasses existed sooner, many of these libraries may have used them. To help the typing systems understand the intent of these kinds of objects, the dataclass transform introduced in Python 3.11 indicates that dataclass-like objects are, well, dataclass-like.
02:02 Like the dataclass transform comes as a decorator, and you can use it with functions, classes, or metaclasses depending on how you are creating the resulting dataclass-like object. Consider the following example.
02:16
If I have a common ModelBase
class that all my serializable objects inherit from, I can decorate that with the dataclass transform. I then create a CustomerModel
class that inherits from that ModelBase
.
02:29 My customer class has two fields, an ID and a name.
02:35
The initializer for my customer model would take the ID and name as parameters in a similar fashion to a dataclass, making it dataclass-like. Because I used the transform on the base class, the CustomerModel
class gets marked as dataclass-like. This is sort of like duck typing, indicating membership based on availability of a feature. In this case, that feature is how the initializer works.
02:59 Mypy or your IDE could now indicate if some of the attributes are missing from the initializer, as they would understand that you intended dataclass-like semantics.
03:12
Two more smaller additions for you. The first is LiteralString
. This type indicates how a string was constructed. A literal is anything that is directly declared as a string.
03:25 A string that is constructed with a concatenation is not a literal. This kind of typing allows you to declare a function that only accepts literal types, which can help you avoid things like SQL injection vulnerabilities.
03:40
The TypedDict
type has two new subtypes that can be used with it: Required[]
and NotRequired[]
. These adapters allow you to declare which keys in the TypedDict
class are mandatory or optional. With every release, there’s always some smaller items. Next up, a bunch of unrelated stuff and things.
Become a Member to join the conversation.