The type of class objects
(Freely after PEP 484: The type of class objects.)
Sometimes you want to talk about class objects that inherit from agiven class. This can be spelled as Type[C]
where C
is aclass. In other words, when C
is the name of a class, using C
to annotate an argument declares that the argument is an instance ofC
(or of a subclass of C
), but using Type[C]
as anargument annotation declares that the argument is a class objectderiving from C
(or C
itself).
For example, assume the following classes:
- class User:
- # Defines fields like name, email
- class BasicUser(User):
- def upgrade(self):
- """Upgrade to Pro"""
- class ProUser(User):
- def pay(self):
- """Pay bill"""
Note that ProUser
doesn’t inherit from BasicUser
.
Here’s a function that creates an instance of one of these classes ifyou pass it the right class object:
- def new_user(user_class):
- user = user_class()
- # (Here we could write the user object to a database)
- return user
How would we annotate this function? Without Type
the best wecould do would be:
- def new_user(user_class: type) -> User:
- # Same implementation as before
This seems reasonable, except that in the following example, mypydoesn’t see that the buyer
variable has type ProUser
:
- buyer = new_user(ProUser)
- buyer.pay() # Rejected, not a method on User
However, using Type
and a type variable with an upper bound (seeType variables with upper bounds) we can do better:
- U = TypeVar('U', bound=User)
- def new_user(user_class: Type[U]) -> U:
- # Same implementation as before
Now mypy will infer the correct type of the result when we callnew_user()
with a specific subclass of User
:
- beginner = new_user(BasicUser) # Inferred type is BasicUser
- beginner.upgrade() # OK
Note
The value corresponding to Type[C]
must be an actual classobject that’s a subtype of C
. Its constructor must becompatible with the constructor of C
. If C
is a typevariable, its upper bound must be a class object.
For more details about Type[]
see PEP 484: The type ofclass objects.