Reputation: 885
I want to create a decorator that adds a member to the decorated class
that is instance of outer class.
In this case, decorator memberclass
adds attribute self
to the instance of class y
that is of type X
class memberclass[Y]:
def __init__(self, cls: type[Y]):
self.cls = cls
def __get__(self, x, cls) -> Y | type[Y]:
if x is None: return self.cls
# I deleted some lines related to memorizing y per instance of X
y = self.cls()
y.self = x
return y
class X:
z: int
@memberclass
class y:
def f(self):
# self.self is instace of X
# type checker should be aware of self.self.z
# X.y is class y
# X().y is instance of that class
# X().y.self is X()
My best guess would be that there might be some way
to annotate __get__
method to say "Y
is getting a new member self
with type typeof x
".
I checked whole documentation https://docs.python.org/3/library/typing.html and didn't find solution.
This also didn't help How to type hint python magic __get__ method since it doesn't show how to modify type
This also didn't help Is it possible to change method type annotations for a class instance? since it modifies function signature, but not the class members
Upvotes: 1
Views: 167
Reputation: 110591
As stated in the comments - there is no way to dynamically add an attribute so that static checkers can "see" it.
What is possible is to hardcode the exta (self
) attribute in the nested class body itself - the static checkers should stop complaining in that case.c
class X:
z: int
@memberclass
class y:
self: "X" # or with Py 3.14, PEP 649, you can let the quotes away
# the thing is it needs to be lazily referenced so that
# X can be seem in the global namespace.
def f(self):
# self.self is instace of X
# type checker should be aware of self.self.z
The decorator could actually recreate the decorated class, making it inherit from an extra base class which would include the self
annotation - but at the time it runs, X
is not yet defined, and, doing so would not resolve the problem - actually, I think that unlike hardcoding the self
annotation, it is not doable at all.
The general hint here is: nested classes are usually not a good thing to do in Python. As you can see, you need this sophisticated descriptor decorator to make it work, because it, naturally, won't work.
If you just define your class outside the body of the container, using the techniques for forward reference that are standard since the beggning of static-type checking (for example, quoting the to-be-defined-class name as a string), and use a plain, one line, property to get your Y
instance as an attribute of X
, that would just work:
class Y:
self: "X"
...
class X:
@property
def Y(self):
... # cache strategy
return Y()
Upvotes: 0