1

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

8
  • The X class doesn't exist when @memberclass is called. The class isn't defined until after the end of the class X: block. Commented Sep 30, 2024 at 20:15
  • Note also that adding self: X to class y would work, but is redundant Commented Sep 30, 2024 at 20:15
  • Nested classes are rarely useful in Python. Commented Sep 30, 2024 at 20:16
  • @Barmar this is not a problem stackoverflow.com/questions/33533148/… Commented Sep 30, 2024 at 20:16
  • 2
    You can’t statically add attributes to a class using a decorator; that requires intersection types, which isn’t developed in Python yet. Commented Sep 30, 2024 at 20:24

1 Answer 1

0

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()
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.