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.

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.


Need Your Help

Excel VBA: Getting subfolders' names as array of strings

excel vba excel-vba

My VBA program code access files inside each subfolder. So I am storing those subfolders' names in an array and using for loop to access. I want to make my program more general. How can we get subf...

How can I disable DatePicker from viewmodel WPF MVVM

c# wpf mvvm datepicker

I am developing a WPF application using WPF, I have a combobox which selected item is bound to viewmodel and i have a datepicker. I want to Disable the datepicker in the view depending on the