Python Class Subscription and __class_getitem__
Learn how Python classes customize square-bracket subscription with __class_getitem__, including generic-style parameterization and the boundary with instance subscription.
Advanced
Python normally uses square brackets to subscribe to an object, but square brackets can also appear directly after a class name. This lesson explains how a class can customize that class-level subscription with
__class_getitem__(). This is the protocol behind parameterized forms such as
list[int] and related generic class syntax.
Give a class a subscription protocol
When Python evaluates
Box[int], the object being subscribed is the class
Box. If the class can use
__class_getitem__(), Python can call that method to decide what the class subscription produces.
|
|
Box[int] calls Box.__class_getitem__(int), so the class decides what that subscription means.
The hook is already a class method
You do not need to write
@classmethod above
__class_getitem__(). Python treats this special method as a class method, so
cls is the class being subscribed.
|
|
The first parameter is the class itself even though no @classmethod decorator is present.
Handle one or several type arguments
The expression inside the brackets can contain one value or a tuple of values. A generic class may therefore accept
Pair[int, str] and normalize the received key before building its result.
|
|
.join(names)}]" print(Pair[int, str]) print(Pair[bytes])
Return a GenericAlias for a generic-style class
For a custom class that follows Python’s generic-type model,
__class_getitem__() can return a
types.GenericAlias. This produces the same kind of parameterized runtime object used by built-in generic classes.
|
|
Returning GenericAlias gives Box[int] a parameterized runtime representation rather than an arbitrary application-specific value.
Keep class and instance subscription separate
Do not confuse
__class_getitem__() with
__getitem__(). The latter handles subscription of an instance. The former can handle subscription when the object being subscribed is itself a class.
|
|
Catalog[int] uses the class-level hook, while catalog[0] uses the instance-level hook.
Inherited class subscription works through the subclass
Because the hook belongs to the class, a subclass can inherit it. The
cls argument then identifies the subclass that was actually subscribed.
|
|
An inherited __class_getitem__ receives Child as cls when Child[float] is evaluated.
A metaclass can take a different path
Class subscription has one important boundary: if the class’s metaclass defines
__getitem__(), that metaclass hook can handle the class subscription instead. In that situation
__class_getitem__() is not necessarily called.
|
|
The metaclass supplies __getitem__(), so that hook handles Product[int] instead of Product.__class_getitem__().
Connect the hook to generic type syntax
Python’s built-in generic classes use class subscription to represent parameterized types. User-defined generic classes can use the modern generic syntax taught in the Generic Type Parameters lesson; this lesson focuses on the lower-level class-subscription protocol and how
__class_getitem__() participates in it.
|
|
Built-in list uses class subscription to produce a parameterized generic alias.
Choose the protocol deliberately
Use __class_getitem__() when a class needs meaningful square-bracket parameterization. For custom generic types, follow Python’s generic-type facilities rather than inventing an unrelated meaning for class subscription.
Key takeaways
- __class_getitem__() handles subscription of a class when the subscription machinery selects the class-level hook.
- Python supplies the subscribed class as cls automatically; no @classmethod decorator is required.
- Multiple bracketed arguments arrive together, commonly as a tuple, so implementations can normalize one-versus-many arguments.
- __getitem__() handles instance subscription, while __class_getitem__() handles a distinct class-subscription path.
- A metaclass __getitem__() can take precedence for class subscription, which is an important boundary of the protocol.
Thanks for your feedback.
Mark this learning resource complete to track your learning progress.