As for example if want let decorated function see logger from closure
def logged(func):
#here i want create logger which i want to be available from decorated function.
@wraps(func)
def _logged(*args, **kwargs):
return func(*args,**kwargs)
#naive idea hot to do it - obviously doesn't work
#return exec('func(*args,**kwargs)',dict(func=func,logger=logger,args=args,kwargs=kwargs),dict(func=func,logger=logger,args=args,kwargs=kwargs))
return _logged
Here's (another) way to do it, but again it's not through a closure which are determined at compile time, so can't be added at runtime. This version adds a global variable to the function's global namespace, taking care to save and restore the value of any similarly named variable that was already there. I got the idea from @Martijn Pieters' answer to the somewhat related question: How to inject variable into scope with a decorator?
from functools import wraps
class Logger:
def __call__(self, *args, **kwargs):
return print(*args, **kwargs)
def logged(func):
logger = Logger() # Get or create value to be added.
varname = 'logger'
sentinel = object()
@wraps(func)
def _logged(*args, **kwargs):
namespace = func.__globals__
oldvalue = namespace.get(varname, sentinel) # Save prev value.
namespace[varname] = logger
try:
return func(*args, **kwargs)
finally:
if oldvalue is sentinel:
del namespace[varname]
else:
namespace[varname] = oldvalue
return _logged
@logged
def myfunc():
logger('Logged from myfunc.')
myfunc() # -> Logged from myfunc.