How can I decorate an instance of a called class?

def decorator(fn): def wrapper(*args, **kwargs): print 'With sour cream and chives!', return fn(*args, **kwargs) return wrapper class Potato(object): def __call__(self): print 'Potato @ {} called'.format(id(self)) spud = Potato() fancy_spud = decorator(Potato()) 

With this code, we have two instances of the called class, one is decorated and one is simple:

 >>> spud() Potato @ 140408136280592 called >>> fancy_spud() With sour cream and chives! Potato @ 140408134310864 called 

I wonder if the use of @decorator syntax for the called only for one instance is supported in any way - as opposed to decorating a class / method that applies to all instances. According to this popular answer, @syntax is just sugar for:

 function = decorator(function) 

But is this oversimplification? For all my attempts with a half-period, it seems that it works only when the syntax occurs before def , class , spaces or @another_decorator .

 @decorator baked = Potato() 

What is a SyntaxError .

 baked = Potato() @decorator baked 

Also SyntaxError .

 @decorator def baked(_spud=Potato()): return _spud() 

It works, but it is ugly and curiously deceiving.

+6
source share
2 answers

You ask questions:

According to this popular answer, @syntax is just sugar for:

function = decorator(function)

However, it’s more accurate to say that

 @decorator def function(): pass 

It is syntactic sugar for:

 def function(): pass function = decorator(function) 

Decorators are intended, in particular, to define definitions of functions, methods, or classes. PEP, which introduced class decorators , describes the grammar:

 decorated: decorators (classdef | funcdef) funcdef: 'def' NAME parameters ['->' test] ':' suite 

As you can see, the decorator should arrive immediately before classdef or funcdef , so there is no way to use it directly in an instance of the called class.

+3
source

Yes, this is a simplification. If we look at the grammar , decorator appears only in the decorators rule, which appears only as part of a classdef or funcdef :

 decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE decorators: decorator+ decorated: decorators (classdef | funcdef) 

What the link to the language says (and I think this is what is repeated in the linked answer) is that

 @f1(arg) @f2 def func(): pass 

equivalently

 def func(): pass func = f1(arg)(f2(func)) 

and similarly for class definitions. But this does not mean that the @decorator syntax can be applied only anywhere; it is valid only before defining a function or class.

As an aside, even official documents are not strictly correct; while the decorator is called a function (or class), it is not tied to the enclosing namespace or scope, therefore, the syntaxes indicated are not entirely equivalent.

Something interesting about the def and class statements, which, in my opinion, are part of the reason that they are the only statements supported by the @decorator syntax: this is the only way in Python to associate a name for an object that knows what that name is.

Finally, here is another way to call a decorator that you might like:

 @decorator class baked: __metaclass__ = lambda *_: Potato() 
+4
source

Source: https://habr.com/ru/post/970573/


All Articles