def log(func):
def wraper(*a, **kw):
return func(*a, **kw)
return wraper
#log
def f():
print 'f'
print locals()['f'] # - prints <function wraper at 0x00CBF3F0>.
How do you get the real f object (not decorator wrap)?
The functools module also provides a wraps decorator which makes sure that the wrapped function looks more like the real function: correct name, module, and docstring, for example.
You don't.1 Store it if you need to access it later.
def log(func):
def wrapper(*a, **kw):
return func(*a, **kw)
wrapper.func = func
return wrapper
#log
def f():
print 'f'
print f.func
1 You could mess with the closure, but I can't recommend it.
If you're running python 3.2 or above, and you use functools.wraps then you will find the wrapped function on the __wrapped__ attribute:
from functools import wraps
def log(func):
#wraps(func)
def wrapper(*a, **kw):
return func(*a, **kw)
return wrapper
#log
def f():
print 'f'
print f.__wrapped__
functools.wrapsis a convenience function for decorating a decorated function with the function that does all the work, including adding this attribute functools.update_wrapper.
Related
I am developing an API through which I am passing to the user list of functionalities of a module with the documentations of each function. In order to access the documentation I used to do:
def foo(*args, **kwargs):
"""
Foo documentation is here!
"""
return None
print(foo.__doc__)
# Foo documentation is here!
Now that I added a decorator for some of those functions, the __doc__ returns None since the decorator function doesn't have any documentation.
def decor_func(func):
def wrap(*args, **kwargs):
return func(*args, **kwargs)
return wrap
#decor_func
def foo(*args, **kwargs):
"""
Foo documentation is here!
"""
return None
print(foo.__doc__)
# None
Is there any way that I can have access to decorated function's documentation?
You can update the __doc__ attribute of the wrap function:
def decor_func(func):
def wrap(*args, **kwargs):
return func(*args, **kwargs)
# Set the decorated function `__doc__` attribute
wrap.__doc__ = func.__doc__
return wrap
#decor_func
def foo(*args, **kwargs):
"""
Foo documentation is here!
"""
return None
print(foo.__doc__)
# Foo documentation is here!
However, the best approach is to use functools.wraps, as allows you to also copy additional attributes such as the original name, module and annotations:
import functools
def decor_func(func):
#functools.wraps(func)
def wrap(*args, **kwargs):
return func(*args, **kwargs)
return wrap
#decor_func
def foo(*args, **kwargs):
"""
Foo documentation is here!
"""
return None
print(foo.__doc__)
# Foo documentation is here!
Note, as others have pointed out, you should use functools.wraps so that your wrapper "looks" like the function it is wrapping, and adds the wrapped fucntion to a __wrapped__ attribute. However, note, you can always introspect the wrapper's closure to retrieve a reference to the original function, since it is a free variable in the wrapper and thus will be stored in the closure:
>>> def decor_func(func):
... def wrap(*args, **kwargs):
... return func(*args, **kwargs)
... return wrap
...
>>> #decor_func
... def foo(*args, **kwargs):
... """
... Foo documentation is here!
... """
... return None
...
>>> foo.__closure__
(<cell at 0x10e69da90: function object at 0x10e83a700>,)
So,
>>> foo.__closure__[0].cell_contents.__doc__
'\n Foo documentation is here!\n '
But again, you should use functools.wraps to begin with. The above might help if you have no control over the decorator though.
I am decorating a function foo with a_decorator
#a_decorator(params)
def foo(x):
# print('Called',decorator_name)
# other magic
Is there a way to access the name a_decorator inside foo so that I can print
'Called a_decorator'
def a_decorator(some_function):
def wrapper():
some_function()
return some_val
return wrapper
It can be done by attaching decorator name to wrapper function:
from functools import wraps
def a_decorator(fn):
#wraps(fn)
def wrapper(*args, **kwargs):
val = fn(*args, **kwargs)
# some action
return val
wrapper.decorated_by = a_decorator
return wrapper
#a_decorator
def foo(x):
print(x, foo.decorated_by.__name__)
foo('test') # prints: test a_decorator
Function in python are first class and you can treat them as an object, attach attributes, etc.
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
#log
def now():
print('2017-5')
Why in the middle to nest wrapper function, why not?It's my first questions in stackoverflow and I'm not good at English .so my description hava any problem ,please forgive me .Thanks!!!
def log(func):
print('call %s():' % func.__name__)
return func(*args, **kw)
#log
def now():
print('2017-5')
A bit about decorators:
#decorator
def func():
...
exactly equals to:
func = decorator(func)
As you can notice, in the first option print('call %s():' % func.__name__) will be called any time when your function now is called in your program. In the second option, this print will be called when decorator applies only without calling now.
Nested function is used because decorator replaces the original function with the modified one.
I am writing a bunch of code that has a possibility of mutable outputs, like an arithmetic function where I could have the output be a float or an int. Basically my problem is that if I were to create a decorator for each object type I need (probably seven or eight), I would go insane with the constant repetition of:
def Int(fn):
def wrapper():
return int(fn())
return wrapper
What I want to have is a class like below that would create a decorator based on the name it's instantiated with and it would be a copy of the function above but with the appropriate type modifications.
class Decorator(object):
def __init__(self):
...
...
Int = Decorator()
# Then I can use #Int
Any help would be really appreciated. Thanks.
You cannot have Decorator know what name it will be assigned to. Assignment occurs after instantiation, so the object will have already been created by the time it is assigned a name.
You could however make a decorator that creates decorators dynamically:
from functools import wraps
def set_return_type(typeobj):
def decorator(func):
#wraps(func)
def wrapper(*args, **kwargs):
return typeobj(func(*args, **kwargs))
return wrapper
return decorator
You would then use this decorator by giving a type object argument for the type you want:
#set_return_type(int) # Causes decorated function to return ints
#set_return_type(float) # Causes decorated function to return floats
Below is a demonstration:
>>> from functools import wraps
>>> def set_return_type(typeobj):
... def decorator(func):
... #wraps(func)
... def wrapper(*args, **kwargs):
... return typeobj(func(*args, **kwargs))
... return wrapper
... return decorator
...
>>> #set_return_type(float)
... def test():
... return 1
...
>>> test()
1.0
>>>
In the below code, how would I obtain *args and **kwargs in function f without the need for the wrapper function?
def f(func):
def wrapper(*args, **kwargs):
print(args)
print(kwargs)
return func(*args,**kwargs)
return wrapper
#f
def write(text):
print(text)
# write = a(write)
write('dog')
Failed attempt 1:
def f(func):
a=func(*args)
k=func(**kwargs)
which causes error:
NameError: global name 'args' is not defined
Failed attempt 2:
def f(func(*args,**kwargs)):
a=func(*args)
k=func(**kwargs)
The wrapper function is necessary, and a standard part of how decorator definitions in Python work.
You can, however, help mask the existence of the wrapper function in tracebacks by using functools.wraps():
import functools
def f(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
This will update the wrapper function to have the name and docstring of the wrapped function.
--
Decorators are nothing more than functions which are passed a function. This code...
def dec(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
#dec
def myfunc(foo, bar):
return foo+bar
is equivalent to this code:
def dec(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
def myfunc(foo, bar):
return foo+bar
myfunc = dec(myfunc)
Notice how the thing being passed to dec is a function which hasn't even been called yet - so there aren't any arguments passed at the time when dec is invoked. This is why the wrapper function is involved: it adds a layer which will be called when the original function is invoked which can capture arguments.