Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python decorator makes function forget that it belongs to a class

I am trying to write a decorator to do logging:

def logger(myFunc):     def new(*args, **keyargs):         print 'Entering %s.%s' % (myFunc.im_class.__name__, myFunc.__name__)         return myFunc(*args, **keyargs)      return new  class C(object):     @logger     def f():         pass  C().f() 

I would like this to print:

Entering C.f 

but instead I get this error message:

AttributeError: 'function' object has no attribute 'im_class' 

Presumably this is something to do with the scope of 'myFunc' inside 'logger', but I've no idea what.

like image 228
Charles Anderson Avatar asked Nov 20 '08 17:11

Charles Anderson


People also ask

What is true about decorators in Python?

Decorators are a very powerful and useful tool in Python since it allows programmers to modify the behaviour of a function or class. Decorators allow us to wrap another function in order to extend the behaviour of the wrapped function, without permanently modifying it.

How do function decorators work in Python?

Python Decorators Summary Decorators dynamically alter the functionality of a function, method, or class without having to directly use subclasses or change the source code of the function being decorated. Using decorators in Python also ensures that your code is DRY(Don't Repeat Yourself).

Can a class method be a decorator Python?

In Python, decorators can be either functions or classes. In both cases, decorating adds functionality to existing functions. When we decorate a function with a class, that function becomes an instance of the class. We can add functionality to the function by defining methods in the decorating class.

Which decorator is used to declare a class method?

In Python, the @classmethod decorator is used to declare a method in the class as a class method that can be called using ClassName. MethodName() . The class method can also be called using an object of the class. The @classmethod is an alternative of the classmethod() function.


1 Answers

Claudiu's answer is correct, but you can also cheat by getting the class name off of the self argument. This will give misleading log statements in cases of inheritance, but will tell you the class of the object whose method is being called. For example:

from functools import wraps  # use this to preserve function signatures and docstrings def logger(func):     @wraps(func)     def with_logging(*args, **kwargs):         print "Entering %s.%s" % (args[0].__class__.__name__, func.__name__)         return func(*args, **kwargs)     return with_logging  class C(object):     @logger     def f(self):         pass  C().f() 

As I said, this won't work properly in cases where you've inherited a function from a parent class; in this case you might say

class B(C):     pass  b = B() b.f() 

and get the message Entering B.f where you actually want to get the message Entering C.f since that's the correct class. On the other hand, this might be acceptable, in which case I'd recommend this approach over Claudiu's suggestion.

like image 194
Eli Courtwright Avatar answered Oct 04 '22 20:10

Eli Courtwright