Description
While playing around with some examples, I saw this type error reported by mypy:

I dug into a bit and learned about the Graphic
/GraphicFeature
relationship. Apologies if you already know this and specifically decided against it... but the general pattern of an abstract _set
method on GraphicFeature
objects that retain a pointer to some parent object looks very much like it could be replaced/improved with python descriptors.
For example, the current pattern (highly simplified) is:
class GraphicFeature:
def __init__(self, parent):
self._parent = weakref.proxy(parent)
def _set(self, value):
raise NotImplementedError
class SomeFeature(GraphicFeature):
def _set(self, value):
print(f"setting {self.name!r} on {self._parent} to {value!r}")
class Graphic:
def __setattr__(self, key, value):
if hasattr(self, key):
attr = getattr(self, key)
if isinstance(attr, GraphicFeature):
attr._set(value)
return
super().__setattr__(key, value)
class SomeGraphic(Graphic):
def __init__(self) -> None:
self.feature = SomeFeature()
with the descriptor pattern, that all reduces to
class SomeFeature:
def __get__(self, instance, owner):
...
def __set__(self, obj, value):
print(f"setting feature on {obj} to {value!r}")
class SomeGraphic:
feature = SomeFeature()
and when you set the feature
attribute on an instance of SomeGraphic
, then SomeFeature.__set__
will be called with the instance and value:
In [18]: g = SomeGraphic()
In [19]: g.feature = 1
setting feature on <__main__.SomeGraphic object at 0x106c33750> to 1
this also works very nicely for typing:

note that it neither complains when setting .feature = [1,2,3]
, and it also recognizes that accessing .feature
will return a str
(i.e. it's been coerced from list to some internal value), based on the type hints of __set__
and __get__