[Spec] Modifying a ClassVar
on an instance (original) (raw)
According to the typing spec:
The typing.ClassVar type qualifier is used to annotate class variables that should not be set on class instances.
However, what about modifying the ClassVar
with __setitem__
or __setattr__
on an instance? It feels like this should also not be allowed. Two examples:
__setitem__
example: pyright playground, mypy playground, ty playground
from typing import ClassVar
class Foo:
d: ClassVar[dict[str, object]] = {}
def set_value(self, key: str, value: object) -> None:
self.d[key] = value # <-- Use Foo.d[key] = value instead?!
__setattr__
example: pyright playground, mypy playground, ty playground
from typing import ClassVar
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
class Bar:
p: ClassVar[Point] = Point(0.0, 0.0)
def set_x(self, value: float) -> None:
self.p.x = value # <-- Use Bar.p.x = value instead?!
Enforcing that setting attributes/items should only be done on the class and not the instance level would be a good reminder that such modification does indeed apply to all instances.
JamesParrott (James Parrott) May 26, 2025, 10:49am 2
It’s more intention revealing to write it your way. And even to refactor both methods into classmethods. It’d be a great optional rule for a linter to warn about. And for a different ClassOnlyVar typequalifier.
But this would prohibit combining classes with ClassVar
s marked with mixins that use those class vars. A lot of extensible code will deliberately refer to an class variable on self, to let a subclass override it with an instance variable. It might also wish to use ClassVar, simply to prevent accidental reassignment.
Do you have an example? Note that the idea of this proposal is to flag
self.class_var.x.y.z[...] = ...
self.class_var.x.y.z = ...
Simply accessing/reading the ClassVar
via self.class_var
is not affected in any way.
(Basically, ClassVars
that are accessed on an instance should be treated as immutable).
NeilGirdhar (Neil Girdhar) May 27, 2025, 10:31pm 4
I believe networkx used to (and probably still does) do this. I agree with you however, and think it’s simply poor design.