Convert all Input parameters of function with generic function - python

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):
...

Related

Get rid of checking len(list) and call functions with/without args

I've got a list, smth like this
['func1', parm1]
or
['func2']
In some cases, there is no second element in the list. The method supposed to call has **kwargs argument, like def func1(self, **kwargs)
My current code is
cmd = matrix[i][0]
value = None
len_matrix = len(matrix[i])
if len_matrix > 1:
value = matrix[i][1]
method = getattr(obj, cmd)
if len_matrix > 1:
method(x=value)
else:
method()
You could use pop(0) on the list to extract the first parameter (the function name). This mutates the list so it will just contain the arguments (if any).
def example(a="a", b="b"):
return a, b
cmd = ["example", "arg_a", "arg_b"]
def caller(args):
func_name = args.pop(0)
func = globals()[func_name]
return func(*args)
caller(cmd)
I don't think it will improve readability, but you can achieve this by creating your kwargs dictionary dynamically.
getattr(obj, matrix[i][0])(**({"x": matrix[i][1]} if len(matrix[i]) > 1 else {}))
If you possibly have more arguments you could extend that more or less easily:
arg_names = "xyzabc"
getattr(obj, matrix[i][0])(**{
arg_names[it]: val for it, val in enumerate(matrix[i][1:])
})
Obviously you could also use arg_names[it - 1]: matrix[i][it] for it in range(1, len(matrix[i]))
Hope that helped.

How to include identical if block in multiple functions?

I'm sorry to ask such a basic question, but what's the Pythonic way to include the same if block that can conditionally return in multiple functions? Here's my setup:
def a():
if bool:
return 'yeehaw'
return 'a'
def b():
if bool:
return 'yeehaw'
return 'b'
I'd like to factor the common conditional out of the two functions, but I'm not sure how to do so.
Use a decorator or closure
def my_yeehaw(result):
def yeehaw():
if some_bool:
return 'yeehaw'
return result
return yeehaw
a = my_yeehaw('a')
b = my_yeehaw('b')
You could use a lambda that takes in a. bool and a default value to return if the condition is false:
check = lambda condition, default: 'yeehaw' if condition else default
def a():
return check(condition, 'a')
def b():
return check(condition, 'b')
I am new to python but I think you can use a default argument to send a or b based on what is passed to the function.
def a(x='a'):
if condition: #where condition can be True or False
return 'yeehaw'
return x
(note: my naming wasn't the best, consider that same_bool function might be better called identical_if_block(...) to follow your example
And I am also assuming bool_ is a parameter, though it could work as a global. But not as bool which, like any function object, is always Truthy
>>> bool(bool)
True
)
Use a function, as long as it doesn't need to return falsies.
def same_bool(bool_):
" works for any result except a Falsy"
return "yeehaw" if bool_ else None
def a(bool_):
res = same_bool(bool_)
if res:
return res
return 'a'
def b(bool_, same_bool_func):
#you can pass in your boolean chunk function
res = same_bool_func(bool_)
if res:
return res
return 'b'
print ("a(True):", a(True))
print ("a(False):", a(False))
print ("b(True, same_bool):", b(True,same_bool))
print ("b(False, same_bool):", b(False,same_bool))
output:
a(True): yeehaw
a(False): a
b(True, same_bool): yeehaw
b(False, same_bool): b
If you do need falsies, use a special guard value
def same_bool(bool_):
" works for any result"
return False if bool_ else NotImplemented
def a(bool_):
res = same_bool(bool_)
if res is not NotImplemented:
return res
return 'a'
You could also feed in "a" and "b" since they are constant results, but I assume that's only in your simplified example.
def same_bool(bool_, val):
return "yeehaw" if bool_ else val
def a(bool_):
return same_bool(bool_, "a")
I ended up liking the decorator syntax, as the functions that include the duplicative conditional logic have a good deal else going on in them:
# `function` is the decorated function
# `args` & `kwargs` are the inputs to `function`
def yeehaw(function):
def decorated(*args, **kwargs):
if args[0] == 7: return 99 # boolean check
return function(*args, **kwargs)
return decorated
#yeehaw
def shark(x):
return str(x)
shark(7)

passing functions and its arguments to another function

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

Using decorator yields NameError on a defined function

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])

Python/Django: Are there any decorators to tell that input is not none?

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

Categories