cp1d
cp1d

Reputation: 13

how __name__ change in python decorator

Recently I'm learning Python decorator and the use of functools.wraps.

def a():
    def b():
        def c():
            print('hello')
        return c
    return b


print a.__name__
#output:a

I understand why the output is a.But I don't know how __name__ change in the following code.

def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

@log('...')
def simple():
    print('*' * 20)

print simple.__name__
#output:wrapper

Why the output is 'wrapper' rather than 'decorator' or 'log'?

Upvotes: 1

Views: 2362

Answers (3)

Łukasz Rogalski
Łukasz Rogalski

Reputation: 23213

Some basics:

@decorator
def f():
    pass

is equivalent to:

def f():
    pass
f = decorator(f)

Decorator with args:

@decorator(*args, **kwargs)
def f():
    pass

is equivalent to:

def f():
    pass
decorator_instance = decorator(*args, **kwargs)
f = decorator_instance(f)

With knowing so, we may rewrite your example to:

def simple():
    print('*' * 20)

log_instance = log('...')
simple = log_instance(simple)

Let's analyze what happens in last two lines:

  • log_instance is a decorator function, and text variable within it is equal to '...'
  • Since decorator (regardless of text value) returns function named wrapper, simple is replaced with function named wrapper

Upvotes: 2

Tobias Kienzler
Tobias Kienzler

Reputation: 27403

@log('...')
def simple(...

is equivalent to

def simple(...
simple = log('...')(simple)

so log is actually called, returning decorator, which is called with simple as argument which is then replaced by decorator's return value, which is the function wrapper, thus its __name__ is wrapper.

Upvotes: 0

BlackBear
BlackBear

Reputation: 22979

The point of decorators is to replace a function or class with what is returned by the decorator, when called with that function/class as argument. Decorators with arguments are a bit more convoluted, as you first call the outer method (log) to obtain a "parameterized" decorator (decorator), then you call that one and obtain the final function (wrapper), which will replace the decorated function (simple).

So, to give it some structure,

  1. call log with '...' as argument and obtain decorator
  2. call decorator with simple as argument and obtain wrapper
  3. replace simple with wrapper

Upvotes: 0

Related Questions