I have been experimenting with decorators and found an interesting inconsistency with them, hope you might help me to resolve it.
To begin with I had a decorator like this:
>>> def name(n):
... def decorator(fun):
... fun.name = n
... return fun
... return decorator
and I used it like so:
>>> @name("my name jeff")
... def f():
... print f.name
since decorator returns fun I could do both:
>>> f()
my name jeff
>>> f.name
'my name jeff'
This was all fine and what I expected. Now comes the weird bit. My new decorator is as follows:
>>> def name(n):
... def decorator(fun):
... fun.name = n
... def wrapper():
... return fun()
... return wrapper
... return decorator
To me it looks like this should do the same thing as the one before it, however I get this:
>>> @name("my name jeff")
... def f():
... print f.__name__
... print f.name
...
>>> f()
wrapper
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in wrapper
File "<stdin>", line 4, in f
AttributeError: 'function' object has no attribute 'name'
Whats even more weird is the following:
>>> def f():
... print f.__name__
... print f.name
...
>>> x = name("jeff")(f)
>>> x.name
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute 'name'
>>> x()
f
jeff
Of course, x.name now fails since decorator returns wrapper and not fun. At the same time:
>>> f = name("jeff")(f)
>>> f()
wrapper
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in wrapper
File "<stdin>", line 3, in f
AttributeError: 'function' object has no attribute 'name'
Furthermore:
>>> def name(n):
... def decorator(fun):
... fun.name = n
... @wraps(fun)
... def wrapper():
... return fun()
... return wrapper
... return decorator
...
>>> @name("my name jeff")
... def f():
... print f.__name__
... print f.name
...
>>> f()
f
my name jeff
Im not much of a python ninja so if Im missing something obvious please point it out.
Your decorator is as follows:
>>> def name(n):
... def decorator(fun):
... fun.name = n
... def wrapper():
... return fun()
... return wrapper
... return decorator
You're setting the name
attribute for fun
, but you're returning wrapper
.
In other words, you replace fun
by wrapper
, that indeed has no name
attribute.
You could try the following:
>>> def name(n):
... def decorator(fun):
... def wrapper():
... return fun()
... wrapper.name = n
... return wrapper
... return decorator
Example:
>>> @name("hello")
... def f():
... print(f.__name__)
... print(f.name)
...
>>> f()
wrapper
hello