Reputation: 571
I am interested in creating a generic function and decorating all other functions with it. The new function will contain arguments from both functions. Example
def decorator(func):
# Some chunk of code
@decorator
def func(a, b):
print a**2, b**2
return a**2, b**2
# Equivalent func of what I want out of the decorator
def equiv_func(a, b, var):
print var # I want to modify all decorated functions to accept a new
# argument and print it using "print var"
print a**2, b**2
return a**2, b**2
This is what I kind of came up with but...
def decor(func):
def modify(var, **kwargs):
print var
x, y = func(**kwargs)
return x, y
return modify
@decor
def func(a, b):
print a**2, b**2
return a**2, b**2
>>> func(var="hi", a=4, b=5)
hi
16 25
I used **kwargs
as a way to account for arguments of func
, but this forces me to use func(var="hi", a=4, b=5)
instead of func(a=4, b=5, var="hi")
due to the positioning of **kwargs
.
Also, this workaround prevents me from using **kwargs
when defining my decorator when I need it since it is already used to input arguments into func
. Example
def test(**kwargs):
if 'test_ans' in kwargs:
if kwargs['test_ans'] == 'y':
return True
else:
return False
def decor(func):
def modify(var, **kwargs):
if test(**kwargs): # I need to use **kwargs here, but I also need it to define my
# arguments in func.
print var
x, y = func(**kwargs)
return x, y
return modify
...
# The following is what I expect but due to **kwargs being used for two purposes,
# it doesn't work
>>> func(var='hi', a=4, b=5, test_ans='y')
'hi'
16 25
>>> func(var='hi', a=4, b=5, test_ans='n')
16 25
The examples are just to illustrate of the constraints I face. They are not the actual code (I wouldn't write a test function that uses **kwargs
for example).
To put simply, is there any other way to modify func(a, b)
to func(a, b, c, d, e, ..., **kwargs)
without using kwargs in the decorator?
Upvotes: 2
Views: 7685
Reputation: 457
You can access the parameters that a function accepts with func.func_code.co_varnames
. Once you know that, you can filter everything else out.
This is limited in that you would not be able to use this effectively if myfunc
accepted **kwargs
as a parameter.
def print_decorator(func):
def wrapped(*args, **kwargs):
# a tuple of the names of the parameters that func accepts
func_params = func.func_code.co_varnames
# grab all of the kwargs that are not accepted by func
extra = set(kwargs.keys()) - set(func_params)
for kw in extra:
print kwargs.pop(kw)
return func(*args, **kwargs)
return wrapped
@print_decorator
def myfunc(a, b, c=None):
print 'myfunc was passed: a={}, b={}, c={}'.format(a, b, c)
return a, b, c
>>> myfunc(1, b=2, c=3, d=4, e=5)
4
5
myfunc was passed: a=1, b=2, c=3
Upvotes: 4
Reputation: 879143
You could allow func
to be called with
func(a=4, b=5, var="hi")
by making var
a keyword argument.
def decor(func):
def modify(*args, **kwargs):
var = kwargs.pop('var', None)
print var
x,y=func(*args, **kwargs)
return x,y
return modify
@decor
def func(a,b):
print a**2,b**2
return a**2,b**2
func(a=4, b=5, var="hi")
func(a=4, b=5)
Upvotes: 2