Compact way to pass same value to arguments - python

When I have a function with bunch of optional arguments
def function_name(arg1=0, arg2=0, arg3=0, arg4=0, ...):
.....doing anything
and I want to pass same value to some of them
function_name(arg1=1, arg3=1, arg6=1)
Is there a more compact way to do it?
so I don't have to keep copying =value and filling up the line.
After several helpful replies I think I came to the answer I was looking for:
def myargs(value, *args):
kargs = {}
for arg in args:
kargs[arg] = value
return kargs
function_name(**myargs(1, 'arg1', 'arg3', 'arg6'))
Thanks guys, sorry if I was bad at explaining what I needed.

I think what you are wanting to do is keyword argument unpacking. You can pass a dictionary to the function and it will unpack the arguments you have included. Anything not included uses the default values.
def test(w=0, x=0, y=0, z=0):
return w+x+y+z
kwargs_1 = {'y': 1}
kwargs_2 = {'x': 2, 'z': 3}
test(**kwargs_1)
# returns:
1
test(**kwargs_2)
# returns:
5

You can use default arguments:
def func(arg1, arg2, def_arg1="1", def_arg2="2"):
pass

You can just use **kwargs
def function_name(arg, argOpt1=0, argOpt2=0, **kwargs):
pass
kwargs = {"argOpt1" : 123, "argOpt2" : 234}
function_name('argument1', kwargs)
Look at this
and this

If the arguments that are to have the same value are contiguous, then:
def function_name(arg1=0,arg2=0,arg3=0,arg4=0):
print(arg1, arg2, arg3, arg4)
function_name(2, *[1] * 3)
Prints:
2 1 1 1

If you didn't write that function, and you can't change its signature, you can pass the right number of arguments that way :
function_name(*(0,) * function_name.__code__.co_argcount)
It will pass the same value (in that case 0) to all the arguments the function expects.
If you need to pass only 10 arguments for instance, you can write :
function_name(*(0,) * 10)
If you want to target specific arguments by their position and not their names, you can use this :
import inspect
def func(a, b, c=None, d='foo', e=False):
print(a, b, c, d, e)
def fill_args(fun, value, arg_numbers):
return {
name: value
for name in (
tuple(inspect.signature(fun).parameters)[i]
for i in arg_numbers
)
}
if __name__ == '__main__':
func(**fill_args(func, 0, (0, 1, 2)))
func(**fill_args(func, 'bar', (0, 1, 4)))
func(**fill_args(func, True, (0, 1, 3)))

Related

Retrieving function arguments from *args and **kwargs

Let's say I have a function f() which I know accepts 1 argument, action, followed by a variable number of arguments.
Depending on the initial action value, the function expects a different set of following arguments, e.g. for action 1 we know we must have 3 extra arguments p1, p2 and p3. Depending on how the function was called, these args can be either in args or `kwargs.
How do I retrieve them?
def f(action, *args, **kwargs):
if action==1:
#we know that the function should have 3 supplementary arguments: p1, p2, p3
#we want something like:
#p1 = kwargs['p1'] OR args[0] OR <some default> (order matters?)
#p1 = kwargs['p1'] OR args[0] OR <some default>
#p1 = kwargs['p1'] OR args[0] OR <some default>
Note that:
f(1, 3, 4, 5)
f(1,p1=3, p2=4, p3=5)
f(1, 2, p2=4, p3=5)
will place the different p1, p2, p3 parameters in either args or kwargs. I could try with nested try/except statements, like:
try:
p1=kwargs['p1']
except:
try:
p1=args[0]
except:
p1=default
but it does not feel right.
I would call a sub-function with a defined argument list within f:
def f(action, *args, **kwargs):
if action == 1:
f_1(*args, **kwargs)
...
def f_1(p1, p2, p3):
# now you know the args
Of course, this raises the question why you need the over-all function, but let's assume you have a reason for that ;). Else, you could also have a dictionary:
funcs = {1: f_1, 2: ....}
Then you can call the correct function with individual arguments as
funcs[action](p1, p2, p3)
I feel you were quite close to the answer on your question in out of itself.
You can do something like this:
def f(action, *args, **kwargs):
if action==1:
p1=kwargs.get('p1') or args[0] if len(args)>0 else 'default'
...
The order does matter. In this case if you the key arguments would take priority over positional ones.
Here are some input examples --> value of p1 in the function:
f(1,10)-->10
f(1,2,20)-->2
f(1,p1=10)-->10
f(1,10,p1=2)-->2
f(1,p3=20)-->'default'
You can try with, using OrderedDict:
from collections import OrderedDict
def f(action, *args, **kwargs):
params = OrderedDict({
'p1': 'default',
'p2': 'default',
'p3': 'default',
})
if action == 1:
for index, param in enumerate(params.keys()):
if index < len(args):
params[param] = kwargs[param] if param in kwargs else args[index]
else:
params[param] = kwargs[param] if param in kwargs else params[param]
# Do something with `params`.
f(1, 3, 4, 5)
f(1, p1=3, p2=4, p3=5)
f(1, 2, p2=4, p3=5)
If you need more parameters, just add them to the ordered dictionary.

Optimizing extraction of kwargs from function with logging decorator

What's the best way NOT to have to extracts the SAME KWARGS twice: once in the decorator (wrapper function) and once in the function itself.
Here is the function:
#close_logger
def close(**kwargs):
""" returns close object"""
# Get params
session_attributes = kwargs.get('sessionAttrbiutes', {})
message = kwargs.get('message', '')
LID = kwargs.get('LIData', {})
SANS = kwargs.get('SANS', [])
FS = kwargs.get('fulfillmentState', 'Fulfilled')
response = {
'sessionAttributes': session_attributes,
'dialogAction': {
'type': SANS,
'fulfillmentState': FS,
'message': {
'contentType': LID,
'content': message
}
}
}
return response
and here is the decorator (used for logging the close event):
def close_logger(func):
#functools.wraps(func)
def wrapper(**kwargs):
# Get params
session_attributes = kwargs.get('sessionAttrbiutes', {})
message = kwargs.get('message', '')
LID = kwargs.get('LIData', {})
SANS = kwargs.get('SANS', [])
FS = kwargs.get('fulfillmentState', 'Fulfilled')
logger.debug('Logging:\n Function:{} Session Attributes: {}\n \
Message{}\n: LID: {}\n SANS: {}\n FS: {}'.format(
func.__name__,
session_attributes,
message,
LID,
SANS,
FS
))
return func(**kwargs)
return wrapper
Start by making your keyword arguments explicit for the close function.
def close(sessionAttrbiutes=None, message='', LIData=None, SANS=None, fulfillmentState='Fulfilled'):
if sessionAttrbiutes is None:
sessionAttrbiutes = {}
...
Note that I used None as default values for the mutable default values to avoid a common pitfall.
Then in your decorator use inspect.getfullargspec, similar to this answer:
import inspect
import functools
def get_default_args(func):
"""
returns a dictionary of arg_name:default_values for the input function
"""
argspec = inspect.getfullargspec(func)
return dict(zip(reversed(argspec.args), reversed(argspec.defaults)))
def close_logger(func):
#functools.wraps(func)
def wrapper(**kwargs):
kwargs_local = get_default_args(func)
kwargs_local.update(kwargs)
logger.debug("""Logging:
Function: {}
Session Attributes: {sessionAttrbiutes}
Message: {message}
LID: {LIData}
SANS: {SANS}
FS: {fulfillmentState}""".format(func.__name__, **kwargs_local))
return func(**kwargs)
return wrapper
This will raise a KeyError if one of those fields is not defined (either in the signature or the passed keywords). Of course it will show None for those where that is the default in the signature.
The full argspec will also contain positional arguments, so you might be able to figure those out as well.
**-notation can be used within function argument lists to both pack and unpack arguments.
**-notation within a function definition collects all the values into a dictionary.
>>> def kwargs_in_definition(**kwargs): return kwargs
>>> kwargs_in_definition(arg1 = 1, arg2 = 2, arg3 = 3)
{'arg1': 1, 'arg2': 2, 'arg3': 3}
**-notation within a function call unpacks all the values as keyword argumnts.
def kwargs_in_call(arg1 =0, arg2 = 0, arg3 = 0): return arg1, arg2, arg3
So when you pass kwargs (kwargs := {'arg1': 1, 'arg2': 2, 'arg3': 3}) to kwargs_in_call you get this:
>>> kwargs_in_call(kwargs)
({'arg1': 1, 'arg2': 2, 'arg3': 3}, 0, 0) # kwargs is used as arg1.
...but if you unpack it first:
>>> kwargs_in_call(**kwargs)
(1, 2, 3) # kwargs unapcks to "arg1 = 1, arg2 = 2, arg3 = 3"
All of this applies for * as well
Your specific case:
**kwargs in a function defintion gives you a regular old dict. You can turn that dictionary into a list -- however you wish to do that --, unpack that list and pass it on to another function. You'll have to account for ordering though, so it's difficult to make this work for all functions.
I'm not sure why you insist on your decorator function taking keyword arguments while your decorated function doesn't. You can, of-course, do whatever you want, but I would argue against such an approach, because you're decorator function is changing how your actual function behaves.
If you manage to sort those keyword arguments in to a valid argument list you'll be good.
Hope this helps.
EDIT: Oh, I forgot to point out that you're unpacking **kwargs in your decorator function (return func(**kwargs)) and then you're repacking them in your actual function (def close(**kwargs):. I'll be honest, that's a silly thing to do.
EDIT: You can use inspect.getargspec or inspect.signature to get a functions argument list. If you just have your decorator take in *args, you can match each value to a corresponding name from the argument list.

Optional Parameters in **kwargs

I am not able to handle to pass optional parameters in python **kwargs
def ExecuteyourQuery(self, queryStatement, *args, **kwargs):
if self.cursorOBJ is not None:
resultOBJ = self.cursorOBJ.execute(queryStatement, *args,**kwargs)
self.resultsVal = resultOBJ.fetchall()
The below statement works fine when I am calling the function
ExecuteyourQuery('select * from item_table where x = :x and y:y', x, y)
But when I give an extra argument which is not mentioned in the query, like
ExecuteyourQuery('select * from item_table where x = :x and y:y', x, y, z)
It returns 0 as output
When using args as the last parameter of your function, you can pass any number of arguments after the formal arguments, when existing. Args is a tuple.
def my_method(farg, *args):
print('FARG: ', farg)
print('ARGS: ', args)
my_method('Formal Argument', 1, 2, 3)
When using kwargs as the last parameter of your function, you can pass any number of named arguments after the formal arguments, when existing. Kwargs is a dictionary.
def my_method(farg, **kwargs):
print('FARG: ', farg)
print('KWARGS: ', kwargs)
my_method('Formal Argument', kwarg_1 = 1, kwarg_2 = 2, kwarg_3 = 3)
When using both args and kwargs as the last parameteres of your function, you can pass any number of arguments (that will be mapped to args) and named arguments (that will be mapped to kwargs) after the formal arguments, when existing.
def my_method(farg, *args, **kwargs):
print('FARG: ', farg)
print('ARGS: ', args)
print('KWARGS: ', kwargs)
my_method('Formal Argument', 1, 2, 3, kwarg_1 = 1, kwarg_2 = 2, kwarg_3 = 3)
Try this out and see the results in your console, hopefully it will help you solve your original problem :)
another way to do this is by setting a default value for a parameter
def method(one, two, three=3):
print(one)
print(two)
if three != 3: # don't have to use it like this but it is a default value
print(three)
this set a default value if the parameter is not filled
if it is filled it will overrule the default value and use that
method(11, 22) # prints first two parameters
method(23, 54, 89) # prints all three
Not sure if this is what your looking for but it is another way

Pass a list of functions with their corresponding parameters to another function

I am struggling to pass a list of functions with a list of corresponding parameters. I also checked here, but it wasn't very helpful. for example (a naive approach which doesn't work):
def foo(data, functions_list, **kwarg):
for func_i in functions_list:
print func_i(data, **kwarg)
def func_1(data, par_1):
return some_function_1(data, par_1)
def func_2(data, par_2_0, par_2_1):
return some_function_2(data, par_2_0, par_2_1)
foo(data, [func_1, func_2], par_1='some_par', par_2_0=5, par_2_1=11)
Importantly, par_1 cannot be used in func_2, so each function consumes a unique set of parameters.
You could use the function's name as the keyword arguments. When indexing kwargs, you'd use func_i.__name__ as the key.
def foo(data, function_list, **kwargs):
for func_i in function_list:
print(func_i(data, kwargs[func_i.__name__]))
And now,
foo(data, [func_1, func_2], func_1='some_par', func_2=[5, 11])
You could use inspect.getargspec (I assume you use Python 2, you shouldn't use that function in Python 3 because it has been deprecated) to find out which argument names a function has and build a new dictionary based on those:
import inspect
def foo(data, functions_list, **kwargs):
for func_i in functions_list:
newkwargs = {name: kwargs[name]
for name in inspect.getargspec(func_i).args
if name in kwargs}
print(func_i(data, **newkwargs))
def func_1(data, par_1):
return data, par_1
def func_2(data, par_2_0, par_2_1):
return data, par_2_0, par_2_1
>>> data = 10
>>> foo(data, [func_1, func_2], par_1='some_par', par_2_0=5, par_2_1=11)
(10, 'some_par')
(10, 5, 11)
But a better way would be to simply associate parameters with functions that doesn't rely on introspection.
If you want to keep the foo function with that exact same declaration and you don't mind each function receiving the whole set of parameters you could do it like this:
You just need to add to each 'my_*' function the **kwargs parameter.
def foo(data, functions_list, **kwargs):
for my_function in functions_list:
print(my_function(data, **kwargs))
def my_sum(a, b, **kwargs):
return a + b
def my_sub(a, c, **kwargs):
return a - c
foo(0, [my_sum, my_sub], b=3, c=10)
Python automatically parses kwargs setting the b and c parameters where it has the value.
Another approach can be like this:
def foo(data, function_list, **kwargs):
function_dict = {
"func_1": func_1,
"func_2": func_2
}
for func_i in function_list:
print function_dict[func_i](data, **kwargs)
def func_1(data, **arg):
filtered_argument = {key: value for key, value in arg.items() if key.startswith('par_1')}
return list([data, filtered_argument])
def func_2(data, **arg):
filtered_argument = {key: value for key, value in arg.items() if key.startswith('par_2_')}
return list([data, filtered_argument])
data = [1,2,3]
foo(data, ['func_1', 'func_2'], par_1='some_par', par_2_0=5, par_2_1=11)
Output:
[[1, 2, 3], {'par_1': 'some_par'}]
[[1, 2, 3], {'par_2_0': 5, 'par_2_1': 11}]
I am sure that you can improvise your current code as it gets ugly in this way.
I like #COLDSPEED's approach, but want to present yet another solution. Pass always 3 values: function, args, keyword args:
Usage:
foo(
func_1, ('some_par',), {},
func_2, (5, 11), {},
)
Implementation (Python3 syntax):
def foo(*args3):
while args3:
func, args, kwargs, *args3 = args3
func(*args, **kwargs)
An approach would be making the 3rd argument of foo a positional argument and pass in a list of args with functions list:
def foo(data, functions_list, args):
for func, arg in zip(functions_list, args):
print(func(data, arg))
def func1(data, par_1):
return 'func1 called with {}'.format(par_1)
def func2(data, par_2):
return 'func2 called with {}'.format(par_2)
foo('some_data', [func1, func2],
[
{'par_1_1': 11, 'par_1_2': 12},
{'par_2_1': 21, 'par_2_2': 22}
])
zip() is used to map each function with the corresponding args.
Output:
func1 called with {'par_1_1': 11, 'par_1_2': 12}
func2 called with {'par_2_1': 21, 'par_2_2': 22}
You can do it something like that, "close" each parameters for function in a list item and then let "foo" split it backwards:
def foo(data, functions_list, kwarg):
for func_i, args in zip(functions_list, kwarg):
func_i(data, **args)
def func_1(data, par_1):
print("func_1 %s %s" % (data, par_1))
def func_2(data, par_2_0, par_2_1):
print("func_2 %s "
"%s %s" % (data, par_2_0, par_2_1))
data = "Some Data"
foo(data, [func_1, func_2], [{"par_1":'some_par'}, {"par_2_0":5, "par_2_1":11}])

What does this to in python super().__init__(**kwargs)

I know that super is used to call the init method in the superclass, I'm having trouble understanding what kwargs does I know it takes key arguments
what does it do here?
class LoginScreen(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 2
self.add_widget(Label(text = "Username: "))
self.username = TextInput(multiline = False)
self.add_widget(self.username)
self.add_widget(Label(text="Password: "))
self.username = TextInput(multiline=False, password=True)
self.add_widget(self.username)
self.add_widget(Label(text="Two Factor Auth: "))
self.tffa = TextInput(multiline=False, password=True)
self.add_widget(self.tffa)
def __init__(self, **kwargs): packs all of the keyword arguments used in any given call to __init__ into a dict
super().__init__(**kwargs): expands them into keyword arguments again.
It's like a wildcard for function parameters. It can be a lazy way to give the subclass the same parameter signature as the parent without bothering to type all of the possible keyword parameters in again.
Just grab them as a blob and shovel them off to the parent to figure out.
For example you put this :
x = LoginScreen(size=1, blahblah=2, something=3)
We have now:
>>> print(kwargs)
{'size': 1, 'blahblah': 2, 'something': 3}
So if it reaches the following line: super().__init__(**kwargs)
it will be equal to that : super().__init__(size=1, blahblah=2, something=3)
Becarefully: if you didn't put double asterisks "**" it will be equal to one argument as key:
`super().__init__({'size': 1, 'blahblah': 2, 'something': 3})`
And you can also use it like that:
options = [1,2,3]
def func(first=None, second=None, last=None):
print("first arg:", first)
print("second arg:", second)
print("third arg:", last)
func(*options)
Here we use one asterisk since this is a list not a dict so that means expand the options list for each argument, the output will be:
first arg: 1
second arg: 2
third arg: 3
But if we call options without the asterisk.
func(options)
See what's gonna happen:
first arg: [1, 2, 3]
second arg: None
third arg: None
The samething with kwargs except they go like that:
fuction(key1=value1, key2=value2, key3=value3, ...)

Categories

Resources