![](https://secure.gravatar.com/avatar/d67ab5d94c2fed8ab6b727b62dc1b213.jpg?s=120&d=mm&r=g)
On Wed, Aug 11, 2021 at 10:03 PM Larry Hastings <larry@hastings.org> wrote:
Specifically: currently, decorators are called just after the function or class object is created, before it's bound to a variable. But we could change it so that we first bind the variable to the initial value, then call the decorator, then rebind. That is, this code:
@dekor8 class C: ...
would become equivalent to this code:
class C: ... C = dekorate(C)
This seems like it would solve the class self-reference problem--the "Node" example above--when PEP 649 is active.
Critically, the decorator itself would have to be evaluated *before* that assignment. So it would actually have to work out more like: _tmp = dekor8 class C: ... C = _tmp(C) Otherwise things like "@foo.setter" would break.
This approach shouldn't break reasonable existing code. That said, this change would be observable from Python, and pathological code could notice and break. For example:
def ensure_Foo_is_a_class(o): assert isinstance(Foo, type) return o
class Foo: ...
@ensure_Foo_is_a_class def Foo(): ...
This terrible code currently would not raise an assertion. But if we made the proposed change to the implementation of decorators, it would. I doubt anybody does this sort of nonsense, I just wanted to fully flesh out the topic.
You would be here declaring that a @monkeypatch decorator is terrible code. I'm not sure whether you're right or wrong. You may very well be right. def monkeypatch(cls): basis = globals()[cls.__name__] for attr in dir(cls): setattr(basis, attr, getattr(cls, attr)) return basis @monkeypatch class SomeClass: def new_method(self): ... Currently this works, since SomeClass doesn't get assigned yet. This could be made to work across versions by writing it as @monkeypatch(SomeClass) instead (and then the actual class name would become immaterial).
If this approach seems interesting, here's one wrinkle to iron out. When an object has multiple decorators, would we want to re-bind after each decorator call? That is, would
@dekor1 @dekor2 @dekor3 class C: ...
turn into approach A:
class C: ... C = dekor1(dekor2(dekor3(C)))
or approach B:
class C: ... C = dekor3(C) C = dekor2(C) C = dekor1(C)
I definitely think "approach B" makes more sense.
If you're going to do it at all, best go the whole way. Each decorator functions independently, and a decorated class is a class like any other, so approach B makes far more sense to me. ChrisA