Python typing.ClassVar examples

Updated: February 14, 2024 By: Guest Contributor Post a comment

Introduction

The typing module in Python, introduced in Python 3.5, has enhanced static type checking capabilities, making code easier to understand and debug. Among its many features, ClassVar is a type hint used exclusively within classes to declare variables that should be class-level attributes, distinguishing them from instance variables. Understanding how to properly use ClassVar can lead to cleaner and more maintainable code.

Welcome to a detailed guide on typing.ClassVar in Python. In this tutorial, we will explore the concept of ClassVar from the Python typing module with code examples ranging from basic to advanced applications.

Basics of ClassVar

Before diving into code examples, it’s crucial to understand what exactly a ClassVar is. A ClassVar indicates that a variable is a class variable and should not be overwritten in instances. This can be particularly useful when you want to ensure that certain attributes belong strictly to the class and are shared across all its instances.

The syntax for defining a ClassVar is simple:

from typing import ClassVar

class MyClass:
    class_var: ClassVar[int] = 42

This example demonstrates a basic usage where class_var is a class-level attribute of type int and is initialized with the value 42.

Code Example 1: Basic Usage

Let’s start with a simple example:

from typing import ClassVar

class Vehicle:
    wheels: ClassVar[int] = 4

print(Vehicle.wheels) # Output: 4
car = Vehicle()
print(car.wheels) # Output: 4  # Note: Accessing through an instance

In this example, the wheels attribute is declared as a ClassVar[int], explicitly indicating that wheels is a class variable and not an instance variable. It demonstrates that wheels can be accessed both from the class itself and its instances, showing the typical behavior of class variables but with added clarity in the code thanks to typing.

Code Example 2: Ensuring Class-Only Access

Although class variables can be accessed from instances, there might be scenarios where you want to ensure variables are purely class-level. This can be subtly enforced by not initializing these variables outside the class definition. However, Python itself does not prevent instance-level assignment of class variables. Here is an approach to ensure class-level access with the use of property decorators.

from typing import ClassVar

class Person:
    _secret: ClassVar[str] = 'top secret'
    
    @property
    def secret(self) -> str:
        raise AttributeError("'secret' is accessible only at the class level")

try:
    p = Person()
    print(p.secret)
except AttributeError as e:
    print(e)  # Output: 'secret' is accessible only at the class level

print(Person._secret) # Output: top secret

In this example, accessing _secret through an instance of Person results in an error, while accessing it directly through the class is successful. Here, ClassVar is used to indicate that _secret is a class-level variable, and a property decorator with a custom getter is utilized to enforce access restrictions.

Code Example 3: Advanced Usage with Generics

A more advanced application of ClassVar involves using it with generics. Python’s typing module also allows for generic type declarations, enabling dynamic typing based on context. When combined with ClassVar, it allows us to declare class-level variables whose types are generic.

from typing import ClassVar, TypeVar, Generic

T = TypeVar('T')

class Container(Generic[T]):
    value: ClassVar[T]
    
    @classmethod
    def set_value(cls, val: T) -> None:
        cls.value = val

int_container = Container[int]()
int_container.set_value(10)
print(int_container.value)  # Output: 10

str_container = Container[str]()
str_container.set_value('hello')
print(str_container.value)  # Output: hello

This example demonstrates how ClassVar can be combined with generic types to create class-level attributes that are type-safe and flexible. Note, however, that this example is somewhat artificial as generic class variables don’t fully observe generics at runtime due to type erasure in Python, but it illustrates the concept.

Conclusion

Through these examples, we’ve seen how typing.ClassVar enriches our ability to declare and manage class-level attributes in Python, promoting clarity and maintainability in our code. While ClassVar might seem like a small part of the typing module, its proper use can significantly impact the design and functionality of class structures. Embrace ClassVar for clearer, more robust class definitions.