This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author randolf.scholz
Recipients AlexWaygood, berker.peksag, corona10, randolf.scholz, rhettinger, serhiy.storchaka, terry.reedy, wim.glenn
Date 2021-10-11.12:55:13
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1633956914.04.0.414158594898.issue45356@roundup.psfhosted.org>
In-reply-to
Content
Dear Raymond,

I think decorator chaining is a very useful concept. At the very least, if all decorators are functional (following the `functools.wraps` recipe), things work out great -- we even get associativity of function composition if things are done properly!

The question is: can we get similar behaviour when allowing decoration with stateful objects, i.e. classes? This seems a lot more difficult. At the very least, in the case of `@classmethod` I think one can formulate a straightforward desiderata:

### Expectation

- `classmethod(property)` should still be a `property`!
- More generally: `classmethod(decorated)` should always be a subclass of `decorated`!

Using your pure-python versions of property / classmethod from <https://docs.python.org/3.9/howto/descriptor.html>, I was able to write down a variant of `classmethod` that works mostly as expected in conjunction with `property`. The main idea is rewrite the `classmethod` to dynamically be a subclass of whatever it wrapped; roughly:

```python
def ClassMethod(func):
  class Wrapped(type(func)):
      def __get__(self, obj, cls=None):
          if cls is None:
              cls = type(obj)
          if hasattr(type(self.__func__), '__get__'):
              return self.__func__.__get__(cls)
          return MethodType(self.__func__, cls)
  return Wrapped(func)
```

I attached a full MWE. Unfortunately, this doesn't fix the `help` bug, though it is kind of weird because the decorated class-property now correctly shows up under the "Readonly properties" section. Maybe `help` internally checks `isinstance(cls.func, property)` at some point instead of `isinstance(cls.__dict__["func"], property)`?

### Some further Proposals / Ideas

1. Decorators could always have an attribute that points to whatever object they wrapped. For the sake of argument, let's take `__func__`.
   ⟹ raise Error when typing `@decorator` if `not hasattr(decorated, "__func__")`
   ⟹ Regular functions/methods should probably by default have `__func__` as a pointer to themselves?
   ⟹ This could hae several subsidiary benefits, for example, currently, how would you implement a pair of decorators `@print_args_and_kwargs` and `@time_execution` such that both of them only apply to the base function, no matter the order in which they are decorating it? The proposed `__func__` convention would make this very easy, almost trivial.
2. `type.__setattr__` could support checking if `attr` already exists and has `__set__` implemented.
  ⟹ This would allow true class-properties with `getter`, `setter` and `deleter`. I provide a MWE here: <https://mail.google.com/mail/u/0/#label/Python+Ideas/FMfcgzGlkPRbJVRkHHtkRPhMCxNsFHpl>
3. I think an argument can be made that it would be really, really cool if `@` could become a general purpose function composition operator?
  ⟹ This is already kind of what it is doing with decorators
  ⟹ This is already exacltly what it is doing in numpy -- matrix multiplication \*is\* the composition of linear functions.
  ⟹ In fact this is a frequently requested feature on python-ideas!
  ⟹ But here is probably the wrong place to discuss this.
History
Date User Action Args
2021-10-11 12:55:14randolf.scholzsetrecipients: + randolf.scholz, rhettinger, terry.reedy, berker.peksag, serhiy.storchaka, wim.glenn, corona10, AlexWaygood
2021-10-11 12:55:14randolf.scholzsetmessageid: <1633956914.04.0.414158594898.issue45356@roundup.psfhosted.org>
2021-10-11 12:55:14randolf.scholzlinkissue45356 messages
2021-10-11 12:55:13randolf.scholzcreate