1

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'?

2
  • Maybe not so related, but you should have a look at What does functools.wraps do? Commented Sep 21, 2016 at 10:46
  • 2
    Are you aware how decorators works internally? Rewriting it without syntactic sugar helps a lot in comprehending what happens here. Commented Sep 21, 2016 at 10:49

3 Answers 3

2

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
Sign up to request clarification or add additional context in comments.

Comments

0

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

Comments

0
@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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.