In my code, I have many places where I pass a function and its arguments to another function. For debugging purposes, I want to print the name of the function and the list of arguments. For example
def process(f, *args, **kwargs):
print("processing " + print_func(f, args, kwargs))
The expected output for
process(myfunc, 1,2, a="A",b=0) should be "processing myfunc(1,2,a="A", b=0)"
I made some progress printing the args as follows:
def print_func(f, *args, **kwarg):
func_str = f.__name__ + "("
if len(args) > 0:
func_str = func_str + (', '.join(['%.2f']*len(x)) % args)
func_str = func_str + ")"
which in the example above will produce the output processing myfunc(1,2)
The problem I have is how to print the kwargs. I can't figure out a similar solution using a dynamic formatting string for printing it as a sequence of k=v pairs separated by ",".
Any suggestion would be appreciated.
To format args and kwargs, you can simply iterate over them and create your string representation
def process(my_func, *args, **kwargs):
#Iterate over all args, convert them to str, and join them
args_str = ','.join(map(str,args))
#Iterater over all kwargs, convert them into k=v and join them
kwargs_str = ','.join('{}={}'.format(k,v) for k,v in kwargs.items())
#Or using f-strings
#kwargs_str = ','.join(f'{k}={v}' for k,v in kwargs.items()
#Form the final representation by adding func name
return "processing {}({})".format(my_func.__name__, ','.join([args_str,kwargs_str]))
#Or using f-strings
#return f"processing {my_func.__name__}({','.join([args_str,kwargs_str])})"
print(process(my_func, 1,2, a="A",b=0))
The output will be
processing my_func(1,2,a=A,b=0)
You can do it by creating an annotation like this
def printArgs(my_func):
def wrapper(*arg, **kwargs):
print("{}({})".format(my_func.__name__, ','.join([str(arg),str(kwargs)])))
return my_func(*arg, **kwargs)
return wrapper
#printArgs
def a_function_where_you_want_to_print_args(arg1, arg2, arg3="arg3"):
pass
a_function_where_you_want_to_print_args(1,2, arg3="b")
This will print "a_function_where_you_want_to_print_args((1, 2),{'arg3': 'b'})"
Related
I have tree types of sub-functions:
one without any parameters (arguments),
second with one parameter
third with multiple parameters (tuple)
I am trying to pass that functions and its arguments to another function which sum results of all sub-functions and return the sum value.
Parameters in that function should be: names of each sub-function as position arguments (*args) and arguments of each subfunction as key-value arguments (*kvargs).
Example:
def no_arg()
def one_arg(a)
def multiple_args(a, b, c, e, f)
# execution of function_results_sum:
function_results_sum(
no_arg, one_arg, multiple_args,
one_arg=23,
multiple_args=(1, 2, 3, 4, 5))
What i have done so far:
def no_arg():
return 5
def ident(x):
return x
def mult(x, y):
return x * y
def function_results_sum(*args, **kwargs):
return no_arg() + ident(kwargs[ident.__name__]) + mult(*kwargs[mult.__name__])
The code above is passing arguments to each sub-function, but sub-function names are hardcoded. I would like to modify the current code to be able to get function names from *args. Below I wrote a pseudocode expressing more less what i am trying to achieve:
def function_results_sum(*args, **kwargs):
for functionName in args:
result = sum(funcionName(kwargs))
return result
I have already spent all day struggling with that problem, so please don't write me that "using google doesn't hurt" ;)
Something like this would work:
def no_arg():
return 5
def one_arg(x):
return x
def multiple_args(x, y):
return x * y
def function_results_sum(*args, **kwargs):
result = 0
for func in args:
result += func(*kwargs[func.__name__])
return result
Output:
function_results_sum(
no_arg, one_arg, multiple_args,
no_arg=(),
one_arg=(23, ),
multiple_args=(1,5))
33
The only difference between what you are asking is that you have to put args in a tuple to then unpack as args to pass in later.
If you dont want to have to supply anything for no argument functions, you can double check if the func name is in kwargs:
def function_results_sum(*args, **kwargs):
result = 0
for func in args:
if func.__name__ i kwargs:
result += func(*kwargs[func.__name__])
else:
result += func()
return result
Post of R Nar is exactly what I tried to achieve. I added additional if statement to verify if kwarg is an integer or a tuple. Thanks that it is not neccessary to put all **kwargs in a tuple. Thank you guys for help!
def function_results_sum(*args, **kwargs):
result = 0
for func in args:
if func.__name__ in kwargs:
if type(kwargs[func.__name__]) == int:
result += func(kwargs[func.__name__])
elif type(kwargs[func.__name__]) == tuple:
result += func(*kwargs[func.__name__])
else:
result += func()
return result
result = function_results_sum(no_arg, ident, mult, ident=2, mult=(3, 4))
print(result)
by goolging 'python determine number of args for passed function' I found How can I find the number of arguments of a Python function?
I'm pretty sure you don't want the **kwars key, value syntax so I use a func_list regular arg and *args
from inspect import signature
def function_results_sum(func_list, *args):
arg_gen = (e for e in args)
return sum([func(*(next(arg_gen)
for _ in range(len(signature(func).parameters))))
for func in func_list])
function_results_sum([no_arg, ident, mult], 7,8,9)
84
the input can be made flatter by parsing *args for Functions and (presumed) arguments (anything not Type Function)
from inspect import signature
import types
def function_results_sum(*args):
func_gen = (e for e in args if isinstance(e, types.FunctionType))
arg_gen = (e for e in args if not isinstance(e, types.FunctionType))
return sum(func(*(next(arg_gen)
for _ in range(len(signature(func).parameters))))
for func in func_gen)
function_results_sum(no_arg, ident, mult, 10,6,90)
555
order of functions and order of args are important, but separately, can be interleaved:
function_results_sum(no_arg, 10, ident, 6, 90, mult)
Out[399]: 555
Why does this :
def fn(proc, *args, **kwargs):
cache = proc.cache = {}
def cached_execution(cache, *args, **kwargs):
if proc in cache:
if args in cache[proc]:
return cache[proc][args]
res = proc(args)
cache[proc] = {args: res}
return res
return cached_execution(cache, proc, *args, **kwargs)
#fn
def cached_fibo(n):
if n == 1 or n == 0:
return n
else:
return cached_fibo(n-1) + cached_fibo(n-2)
print cached_fibo(100)
throw an exception like this:
NameError: global name 'cached_fibo' is not defined
What fundamental concept am I missing?
(Conceptually, **kwargs is for decoration only. Not utilizing in retrieving the cached result, but don't worry about it).
A decorator should return a function, not the result of calling a function.
But this leads us to the next mistake: when you're passing cache and proc to cached_execution function they land in *args which in turn gets passed to proc. This doesn't make sense. Just let cache and proc be captured within the inner method:
def fn(proc, *args, **kwargs):
cache = proc.cache = {}
def cached_execution(*args, **kwargs):
if proc in cache:
if args in cache[proc]:
return cache[proc][args]
res = proc(*args)
cache[proc] = {args: res}
return res
return cached_execution
Another problem: you were not unpacking args. You should call proc(*args) instead of proc(args) (already fixed above).
The wrapper seems a little malformed. Here is an updated version:
def fn(proc):
cache = proc.cache = {}
def cached_execution(*args, **kwargs):
if proc in cache:
if args in cache[proc]:
return cache[proc][args]
res = proc(args[0])
cache[proc] = {args: res}
return res
return cached_execution
You were trying to run the wrapper function inside the wrapper instead of returning it to be run as the function, causing issues.
The next issue is that the argument you supply is a list of tuples *args at proc(args) when you only want the first one, so needs to turn into proc(args[0])
I am writing a function which gets users from database and returns a list of user objects.
Function signature is as given below:
def select_users(self,userid,firstname,lastname,emailid,tenants,groups):
result = self.authservice.select_users(userid,firstname,lastname,emailid,tenants,groups)
In this function, I call select_users method of authservice object which will return a list of custom user objects. But if any of input parameters has '' value then it must be converted to None because self.authservice.select_users cannot handle empty strings. I can check each element value and convert it to None if it is empty, but I want it to be generic and reusable. If I could write a different function which can give me updated list of input parameters it would be very helpful. Please let me know how do I do that?
I would write a generic decorator, like this
def convert_empty_to_none(func):
def inner_function(*args, **kwargs):
args = (None if item == "" else item for item in args)
kwargs = {k:(None if v == "" else v) for k, v in kwargs.items()}
return func(*args, **kwargs)
return inner_function
#convert_empty_to_none
def test_function(a, b, c):
print a, b, c
test_function("", "", "")
Output
None None None
Evil way:
def select_users(self, *args):
new_args = [(None if arg == '' else arg) for arg in args]
result = self.authservice.select_users(*new_args)
Decorator solution is also pretty evil: changing function arguments to spare writing a couple of function calls doesn't seem such a great idea.
In the real life I would go with the explicit:
def never_blank(s):
return None if s == '' else s
def select_users(self, userid,firstname,lastname,emailid,tenants,groups):
result = self.authservice.select_users(userid,never_blank(firstname),never_blank(lastname),emailid,
never_blank(tenants),groups)
Tedious? Sure. Clean? Yep. Will bite you in the ass in the future? Nope.
Create a function and use it like a function type decorator
def sanitize(func):
def handler(*args, **kwargs):
args = (e if e != '' else None for e in args)
kwargs = {k:(v if v != '' else None) for k, v in kwargs.items()}
return func(*args, **kwargs)
return handler
#sanitize
def select_users(self,userid,firstname,lastname,emailid,tenants,groups):
result = self.authservice.select_users(userid,firstname,lastname,emailid,tenants,groups)
Benifits
You do not need to modify the signature
The caller would still have clear idea, what parameters the function expects
Generic and can be used for any function call
Is a decorator, so can easily be used in a non-intrusive fashion
You could use a decorator to create a generic wrapper that will replace every empty string with None.
def none_for_empty_string(func):
def wrapper(*args, **kwargs):
args = tuple(arg if arg != '' else None for arg in args)
kwargs = {k : v if v != '' else None for k, v in kwargs.iteritems()}
return func(*args, **kwargs)
return wrapper
#none_for_empty_string
def select_users(self,userid,firstname,lastname,emailid,tenants,groups):
...
I am new to Django and come from Java/Spring background.
I am wondering if there are decorators something like following that can be done in Django or Python?
Want
def addToList(#not_none a, #not_none b):
# so that I do not check for nullity explicitly
do_things_with_a(a)
do_things_with_b(b)
Since this is something which is pretty easy to get in Java, just looking if Python/Django has it
One doesn't typically constraint data-types in Python. Also, decorators can only be applied to classes and to methods/functions.
Although, you shouldn't really be doing this, this is how you would.
(You could amend this to accept argument names to enforce constraints on with a little work).
def not_none(f):
def func(*args, **kwargs):
if any(arg is None for arg in args):
raise ValueError('function {}: does not take arguments of None'.format(f.__name__))
return f(*args, **kwargs)
return func
#not_none
def test(a, b):
print a, b
You can write a decorator rejectNone as follows:
def rejectNone(f):
def myF(*args, **kwargs):
if None in args or None in kwargs.values():
raise Exception('One of the arguments passed to {0} is None.'.format(f.__name__)
return f(*args, **kwargs)
return myF
#rejectNone
def f(a, b, k=3):
print a * b
You will now get an Exception if you try to call f with a None argument. Note that decorators can be applied to functions or class methods but you can't put them in front of function parameters.
I know this is late, but to those who it may be helpful.
I have a simple repo based off of Jon's answer that accepts arguments for nullable fields here.
def not_none(nullable_parameters=None):
def the_actual_test(f, args, filter_array):
has_none = False
bad_parameters = []
if type(filter_array) is str:
filter_array = [filter_array]
if not filter_array:
if any(arg[1] is None for arg in args):
raise ValueError('function {}: Parameters cannot be None. '.format(f.__name__))
elif type(filter_array) is list:
for a in args:
for ff in filter_array:
if a[0] != ff:
if a[1] is None:
has_none = True
bad_parameters.append(a[0])
break
if has_none:
raise ValueError('function {}: Parameters {} cannot be None. '.format(f.__name__, bad_parameters))
def real_decorator(f):
v_names = f.__code__.co_varnames
def wrapper(*args, **kwargs):
n_args = []
for a in range(0, len(args)):
n_args.append((v_names[a], args[a]))
the_actual_test(f, n_args, nullable_parameters)
result = f(*args, **kwargs)
return result
return wrapper
return real_decorator
Usage
from not_none import not_none
#not_none()
def no_none(a,b):
return (a,b)
#not_none(nullable_parameters=["b"])
def allow_b_as_none(a,b):
return (a,b)
#passes
no_none(1,1)
#fails
no_none(None,1)
#passes
allow_b_as_none(1,None)
#fails
allow_b_as_none(None,1)
After my first answer got deleted. Here is an updated version:
I tried to use the very nice answer from Bigbob556677, but for me it didn't work with **kwargs, so I edited it and put it in a Gist, here: https://gist.github.com/devTechi/6e633ded72cc83637f34b1a3f4a96984 (code also below)
I didn't test it with just *args, but with (I posted more or less just the gist-link) **kwargs it works nicely.
def not_none(nullable_parameters=None):
# values given by real_decorator (see below)
def the_actual_test(f, expected_args_with_given, allowed_nullable_args):
has_none = False
bad_parameters = []
for key, value in expected_args_with_given.items():
if (value is None and nullable_parameters is None) or \
(value is None and key not in nullable_parameters):
bad_parameters.append(key)
has_none = True
if has_none:
raise ValueError("[Function '{}' of '{}'] - IMPORTANT: Parameters '{}' cannot be None. ".format(f.__name__, f.__module__, bad_parameters))
# here the code REALLY begins
def not_null_decorator(original_func):
import inspect
has_self = False
# f.__code__.co_varnames --> local variables (not only parameters), see: https://python-reference.readthedocs.io/en/latest/docs/code/varnames.html
# get declared arguments from ogirinal function
argspec = inspect.getargspec(original_func)
if 'self' in argspec.args:
argnames = argspec.args[1:] # no self
has_self = True
else:
argnames = argspec.args
args_dict = dict.fromkeys(argnames)
def get_args(*args, **kwargs):
for arg in args:
if arg in args_dict.keys():
args_dict[arg] = arg
for key, value in kwargs.items():
if key in args_dict.keys():
args_dict[key] = value
return args_dict
def wrapper_with_self(self, *args, **kwargs):
the_actual_test(original_func, get_args(*args, **kwargs), nullable_parameters)
return original_func(self, *args, **kwargs)
def wrapper(*args, **kwargs):
the_actual_test(original_func, get_args(*args, **kwargs), nullable_parameters)
return original_func(*args, **kwargs)
if has_self:
return wrapper_with_self
else:
return wrapper
return not_null_decorator
Usage:
from .nullable_decorator import not_none
#not_none(nullable_parameters=["nullable_arg1", "nullable_arg2"])
def some_function(self, nullable_arg1=None, nullable_arg2=None, non_nullable_arg1=None):
pass
#not_none()
def some_other_function(self, non_nullable_arg1=None, non_nullable_arg2=None):
pass
In writing a class recently, I initially included a __repr__ method along the following lines:
return "{}({!r}, {!r}, {!r})".format(
self.__class__.__name__,
self.arg1,
self.arg2,
self.arg3)
Repeating the '{!r}' snippet like that feels wrong and would be tedious to maintain if I ever add any more arguments to this class. However, the more robust alternatives that occurred to me aren't going to win any prizes for elegance either.
Building the format string programmatically:
fmt = "{}(%s)" % ", ".join(["{!r}"]*3)
return fmt.format(self.__class__.__name__,
self.arg1,
self.arg2,
self.arg3)
Formatting the arguments separately with str.join:
args = ", ".join(map(repr, [self.arg1, self.arg2, self.arg3]))
return "{}({})".format(self.__class__.__name__, args)
I've currently implemented the class using the last example, but I'm interested in suggestions for alternative approaches (since I'm not particularly happy with any of the above options).
Update:
Inspired by Ned's answer, I've now added the following utility function to a helper module:
def format_iter(iterable, fmt='{!r}', sep=', '):
return sep.join(fmt.format(x) for x in iterable)
>>> format_iter(range(10))
'0, 1, 2, 3, 4, 5, 6, 7, 8, 9'
>>> format_iter(range(10), sep='|')
'0|1|2|3|4|5|6|7|8|9'
>>> format_iter(range(10), fmt='{:04b}', sep='|')
'0000|0001|0010|0011|0100|0101|0110|0111|1000|1001'
>>> format_iter(range(10), fmt='{0.real}+{0.imag}j')
'0+0j, 1+0j, 2+0j, 3+0j, 4+0j, 5+0j, 6+0j, 7+0j, 8+0j, 9+0j'
Update2:
I ended up adding a second utility function, almost identical to the one in agf's answer:
def call_repr(name, *args):
return "{}({})".format(name, format_iter(args))
So the originally offending __repr__ function now looks like:
def __repr__(self):
return call_repr(self.__class__.__name__,
self.arg1,
self.arg2)
(Yes, one of the original constructor arguments went away earlier today.)
If this is a pattern you're going to repeat, I'd probably use:
# This is a static method or module-level function
def argrepr(name, *args):
return '{}({})'.format(name, ', '.join(repr(arg) for arg in args))
def __repr__(self):
return argrepr(self.__name__, self.arg1, self.arg2, self.arg3)
or
# This is a static method or module-level function
def argrepr(*args):
return '(' + ', '.join(repr(arg) for arg in args) + ')'
def __repr__(self):
return repr(self.__name__) + argrepr(self.arg1, self.arg2, self.arg3)
My inclination would be, if you don't like the code, hide it in a function:
def repr_all(*args):
return ", ".join(repr(a) for a in args)
def __repr__(self):
args = repr_all(self.arg1, self.arg2, self.arg3)
return "{}({})".format(self.__class__.__name__, args)
I might do it like:
def __repr__(self):
return "{0}({1})".format(
type(self).__name__,
", ".join(repr(arg) for arg in (self.arg1, self.arg2, self.arg3)))
or:
_init_getter = operator.attrgetter('arg1', 'arg2', 'arg3')
def __repr__(self):
return "{0}({1})".format(
type(self).__name__,
", ".join(repr(arg) for arg in self._init_getter(self)))