user238424
user238424

Reputation: 1047

Why can @decorator not decorate a staticmethod or a classmethod?

Why can decorator not decorate a staticmethod or a classmethod?

from decorator import decorator

@decorator
def print_function_name(function, *args):
    print '%s was called.' % function.func_name
    return function(*args)

class My_class(object):
    @print_function_name
    @classmethod
    def get_dir(cls):
        return dir(cls)

    @print_function_name
    @staticmethod
    def get_a():
        return 'a'

Both get_dir and get_a result in AttributeError: <'classmethod' or 'staticmethod'>, object has no attribute '__name__'.

Why does decorator rely on the attribute __name__ instead of the attribute func_name? (Afaik all functions, including classmethods and staticmethods, have the func_name attribute.)

Edit: I'm using Python 2.6.

Upvotes: 54

Views: 23526

Answers (3)

user238424
user238424

Reputation: 1047

It works when @classmethod and @staticmethod are the top-most decorators:

from decorator import decorator

@decorator
def print_function_name(function, *args):
    print '%s was called.' % function.func_name
    return function(*args)

class My_class(object):
    @classmethod
    @print_function_name
    def get_dir(cls):
        return dir(cls)
    @staticmethod
    @print_function_name
    def get_a():
        return 'a'

Upvotes: 40

wberry
wberry

Reputation: 19337

classmethod and staticmethod return descriptor objects, not functions. Most decorators are not designed to accept descriptors.

Normally, then, you must apply classmethod and staticmethod last when using multiple decorators. And since decorators are applied in "bottom up" order, classmethod and staticmethod normally should be top-most in your source.

Like this:

class My_class(object):
    @classmethod
    @print_function_name
    def get_dir(cls):
        return dir(cls)

    @staticmethod
    @print_function_name
    def get_a():
        return 'a'

Upvotes: 93

mykhal
mykhal

Reputation: 19905

Is this what you wanted?

def print_function_name(function):
    def wrapper(*args):
        print('%s was called.' % function.__name__)
        return function(*args)
    return wrapper

class My_class(object):
    @classmethod
    @print_function_name
    def get_dir(cls):
        return dir(cls)

    @staticmethod
    @print_function_name
    def get_a():
        return 'a'

Upvotes: 2

Related Questions