I am currently designing a class structure using attrs. It is a tree-like structure where I need a parent backlink for certain purposes. OTOH, I want to use frozen classes (there are cached properties, which rely on read-only state).
This means that the tree must be instantiated bottom-up, child-before parent. To solve this, I invented a special kind of weakref object, which can be lazy-bound exactly once.
class LateRef(Generic[T]):
def __init__(self):
self.ref: Optional[T] = None
def set(self, obj: T):
if self.ref is not None:
raise RuntimeError('Reference can only be set once.')
self.ref = weakref.ref(obj)
def __call__(self) -> Optional[T]:
if self.ref is None:
return None
return self.ref()
There is custom initialization code to create an unbound reference in the child, and bind them in the parent:
@attr.s(auto_attribs=True, frozen=True)
class Child:
some_attribute:str = "whatever"
def __attrs_post_init__(self):
self.__dict__['parent'] = LateRef()
@attr.s(auto_attribs=True, frozen=True)
class Parent:
children: List[Child] = attr.ib(converter=_deepclone) # off-screen :-)
def __attrs_post_init__(self):
for child in self.children:
child.parent.set(self)
I need to sneak in the Child.parent attribute through the backdoor due to the frozenness of child. Previously I put it as a class attribute with custom factory, but then it is turned into an attrs-attribute, which it really shouldn't be. E.g. I don't want the parent to turn up in hashing, comparison, str representation and so on.
The code is already working, BUT: As you see I would like to do things right and type-annotate as far as possible. Putting the parent directly in the __dict__ means that the usual tools don't know the expected type. In fact they might even complain that the parent attribute doesn't exist at all.
Long story short question: Is there a way to declare parent as class attribute with type annotation, while telling attr.s to skip / ignore it? Or is there another solution I am overlooking?
Edit: i.e. How to make a static type checker recognize the parent attribute's type, without making it an attr.ib.