Python class method decorator with native arguments?

How to pass a class field to a class method decorator as an argument? I want to do something like:

class Client(object): def __init__(self, url): self.url = url @check_authorization("some_attr", self.url) def get(self): do_work() 

He complains that self does not exist to pass self.url decorator. Is there a way around this?

+120
python
Jul 30 '12 at 23:30
source share
5 answers

Yes. Instead of passing an instance attribute during class definition, check it at runtime:

 def check_authorization(f): def wrapper(*args): print args[0].url return f(*args) return wrapper class Client(object): def __init__(self, url): self.url = url @check_authorization def get(self): print 'get' >>> Client('http://www.google.com').get() http://www.google.com get 

The decorator intercepts the arguments of the method; the first argument is the instance, so it reads an attribute of this. You can pass the attribute name as a string to the decorator and use getattr if you do not want to hardcode the attribute name:

 def check_authorization(attribute): def _check_authorization(f): def wrapper(self, *args): print getattr(self, attribute) return f(self, *args) return wrapper return _check_authorization 
+166
Jul 30 '12 at 23:38
source share
 from re import search from functools import wraps def is_match(_lambda, pattern): def wrapper(f): @wraps(f) def wrapped(self, *f_args, **f_kwargs): if callable(_lambda) and search(pattern, (_lambda(self) or '')): f(self, *f_args, **f_kwargs) return wrapped return wrapper class MyTest(object): def __init__(self): self.name = 'foo' self.surname = 'bar' @is_match(lambda x: x.name, 'foo') @is_match(lambda x: x.surname, 'foo') def my_rule(self): print 'my_rule : ok' @is_match(lambda x: x.name, 'foo') @is_match(lambda x: x.surname, 'bar') def my_rule2(self): print 'my_rule2 : ok' test = MyTest() test.my_rule() test.my_rule2() 

output: my_rule2: good

+33
May 3 '13 at 11:52
source share

A more concise example might be as follows:

 #/usr/bin/env python3 from functools import wraps def wrapper(method): @wraps(method) def _impl(self, *method_args, **method_kwargs): method_output = method(self, *method_args, **method_kwargs) return method_output + "!" return _impl class Foo: @wrapper def bar(self, word): return word f = Foo() result = f.bar("kitty") print(result) 

Which will print:

 kitty! 
+28
Apr 29 '16 at 18:13
source share

You can not. There is no self in the class because no instance exists. You need to pass it, say str , containing the name of the attribute to search in the instance that the returned function can return, or use another method in its entirety.

+3
Jul 30 '12 at 23:38
source share

Another option is to discard syntactic sugar and decorate it in the __init__ class.

 def countdown(number): def countdown_decorator(func): def func_wrapper(): for index in reversed(range(1, number+1)): print("{}".format(index)) func() return func_wrapper return countdown_decorator class MySuperClass(): def __init__(self, number): self.number = number self.do_thing = countdown(number)(self.do_thing) def do_thing(self): print('im doing stuff!') myclass = MySuperClass(3) myclass.do_thing() 

which would print

 3 2 1 im doing stuff! 
+3
May 27 '19 at 9:10
source share



All Articles