Possible modification to ClassVar

I gave this topic significant thought when developing pyright. After much experimentation and feedback from users, I came up with this formulation. It’s internally consistent — and consistent with the current typing spec, but it differs from mypy in a few ways. It seems to work well for the common use cases in Python.

Setting aside enums, namedtuples, dataclasses, and TypedDicts (which are all non-standard in some respects), a variable in “normal” Python classes falls into one of three categories:

  1. A “pure” class variable that is not intended to be overwritten by an instance variable
  2. A “pure” instance variable that has no class variable associated with it
  3. A class variable that may be overwritten by an instance variable; the value at the class level acts as a default fallback

Pyright allows you to specify which of these three you intend. If you want a “pure” class variable, use ClassVar. In this case, pyright enforces that the variable cannot be set (overwritten) through an instance.

If you want a “pure” instance variable, do not declare the variable in the class body and simply set the value through a self.x = v statement. Alternatively, specify the variable in __slots__. In this case, pyright enforces that the variable cannot be set through the class.

If you want a hybrid, declare (and optionally set the value of) the variable in the class body.

I think this provides a natural way for developers to express their intent. Pyright’s rules differ slightly from the rules that @mikeshardmind proposes at the top of this thread. Based on my experience, the rules that I converged on with pyright are more consistent with developer expectations and common use cases.

3 Likes