
On Wed, May 26, 2021 at 12:43:48PM -0400, Ricky Teachey wrote: [...]
These two ideas of a decorator syntax result are not the same:
RESULT A: function decorator # func = decorator("spam")(func)
RESULT B: variable decorator # name = decorator("spam")("name")
...because func is passed as an object, but "name" a string representing the name of the object. Two very different things.
Ricky, it's not clear to me whether you are proposing the above RESULT A and RESULT B as an *alternative* to the "variable decorator" proposal, or if you have just misunderstood it. The current variable decorator proposal on the table is for this: @decorator(spam) name # --> name = decorator("name", spam) rather than what you wrote: # name = decorator("spam")("name") So I can't tell whether the difference between your version and the OPs is a bug or a feature :-)
For this reason I think I would agree even more so that the differences in the decorator behavior would be an extremely significant point of confusion. [...] Maybe employment of decorator syntax could OPTIONALLY trigger a new dunder method-- here I'll just call it __decoration_call__-- with the signature:
def __decoration_call__(self, obj: Any, by_name: str) -> Any: ...
To be clear here, I think that your proposal is that this method is to be looked up on the *decorator*, not the thing being decorated. Is that correct? In other words: @decorator class X: ... # or a function, or something else it is *decorator*, not X, that is checked for a `__decoration_call__` method. Correct?
My idea is to optionally allow any callable object to write a __decoration_call__ method that gets called in lieu of the __call__ method when the callable object is employed using decorator syntax. When this happens, the decorated named is supplied- not counting self- as the first argument (e.g., by_name), which contains the str value of the name the decorator was applied to.
In current Python, the only objects which can be decorated with the @ syntax are callable functions and classes. So it is ambiguous to talk about "any callable object" without stating whether it is the decorator or the thing being decorated.
In actuality, unless I'm wrong (I might be; not an expert) current decorator syntax is really sugar for:
def func(): ... func = decorator.__call__("spam this").__call__(func)
Roughly speaking, that would correspond to @decorator("spam this") def func(): ... If we have a bare decorator, we have this: @decorator def func(): ... # --> func = decorator.__call__(func)
My proposal is to make it such that:
@decorator def func(): ...
...*can result* in this:
def func(): ... func = decorator.__decoration_call__( func, "func")
Okay. Without reading the source code, does this code snippet use the old `__call__` protocol or the new `__decoration_call__` protocol? @flambé class Banana_Surprise: pass It seems to me that this proposal means that we can't even tell which of the two protocols (classic decoration, or new `__decoration_call__` style decoration) without digging into the implementation of the decorator. To be precise, the problem here as reader isn't so much the fact that I don't know whether the object is called using the `__call__` protocol or the new-style `__decorator_call__` protocol, but the fact that I can't tell whether the calls will involve the name being passed or not. This is because the name is being *implicitly* passed, in a way that is unclear whether or not it will be passed. I just don't know whether or not the decorator `flambé` receives the name or not.
And also so that this:
@decorator("spam this") def func(): ...
...*can result* in this:
def func(): ... func = decorator.__call__("spam this").__decoration_call__(func, "func")
What happens if the decorator factory has `__decoration_call__` and the object it returns only has `__call__`? I presume you get this: func = decorator.__decoration_call__("spam this", "func").__call__(func) And let's not forget the other two combinations: func = decorator.__decoration_call__("spam this", "func").__decoration_call__(func, "func") func = decorator.__call__("spam this").__call__(func) The last one is, of course, the current behaviour for a decorator factory. The bottom line here is that is you have plain, unadorned decorator: @decorator there are two possible behaviours and no obvious way to tell which one is used, short of digging into the implementation. But if you have a decorarator factory: @factory(*args, **kwargs) there are now four possible behaviours. And anyone brave enough to use a double-barrelled factory-factory @factory(*args, **kwargs)(*more_args) will be faced with eight possible combinations. And at this point, I'm afraid I have run out of steam to respond further to this proposal. Sorry. -- Steve