Pass keyword arguments as required arguments in Python - python

I have, for example, 3 functions, with required arguments (some arguments are shared by the functions, in different order):
def function_one(a,b,c,d,e,f):
value = a*b/c ...
return value
def function_two(b,c,e):
value = b/e ..
return value
def function_three(f,a,c,d):
value = a*f ...
return value
If I have the next dictionary:
argument_dict = {'a':3,'b':3,'c':23,'d':6,'e':1,'f':8}
Is posible to call the functions in this way??:
value_one = function_one(**argument_dict)
value_two = function_two (**argument_dict)
value_three = function_three (**argument_dict)

Not the way you have written those functions, no: they are not expecting the extra arguments so will raise a TypeError.
If you define all the functions as also expecting **kwargs, things will work as you want.

I assume what you're trying to do is to create a function with an undefined number of arguments. You can do this by using args (arguments) or kwargs (key word arguments kind of foo='bar') style so for example:
for arguments
def f(*args): print(args)
f(1, 2, 3)
(1, 2, 3)`
then for kwargs
def f2(**kwargs): print(kwargs)
f2(a=1, b=3)
{'a': 1, 'b': 3}
Let's try a couple more things.
def f(my_dict): print (my_dict['a'])
f(dict(a=1, b=3, c=4))
1
It works!!! so, you could do it that way and complement it with kwargs if you don't know what else the function could receive.
Of course you could do:
argument_dict = {'a':1, 'b':3, 'c':4}
f(argument_dict)
1
So you don't have to use kwargs and args all the time. It all depends the level of abstraction of the object you're passing to the function. In your case, you're passing a dictionary so you can handle that guy without only.

Related

Make a Python function that returns the same arguments as it receives

What is a proper way in Python to write a function that will return the very same parameters it received at run-time?
E.g.:
def pass_thru(*args, **kwargs):
# do something non-destructive with *args & **kwargs
return ??? <- somehow return *args & **kwargs
Consider the following function:
def a(*args, **kwargs):
return args, kwargs
When we call the function, the value returned is a tuple, containing first another tuple with the arguments, then a dictionary with the keyword arguments:
b = a(1, 2, 3, a='foo')
print(b)
Outputs: ((1, 2, 3), {'a': 'foo'})
print(b[0]) # Gives the args as a tuple
print(b[1]) # Gives the kwargs as a dictionary
The problem is that your arguments are just a sequence of values, not a value itself you can manipulate. Keyword arguments are not themselves first-class values (that is, a=3 is not a value); they are purely a syntactic construct.
* and ** parameters get you halfway there:
def pass_thru(*args, **kwargs):
return *args, kwargs
Then
>>> pass_thru(1, 2, a=3)
(1, 2, {'a': 3})
but you can't simply pass that back to pass_thru; you'll get a different result.
>>> pass_thru(pass_thru(1,2,a=3))
((1, 2, {'a': 3}), {})
You can try unpacking the tuple:
>>> pass_thru(*pass_thru(1,2,a=3))
(1, 2, {'a': 3}, {})
but what you really need is to unpack the dict as well. Something like
>>> *a, kw = pass_thru(1,2,a=3)
>>> pass_thru(*a, **kw)
(1, 2, {'a': 3})
As far as I know, there is no way to combine the last example into a single, nested function call.

Does type coercion have anything to do with why dict(c=c) works in Python?

Consider the following code:
c = 3
dict(c=c) # result: {'c': 3}
I'm interested to know what's going on here in this constructor. c is the name of a variable, but it ends up being a string in the keys of the dict. Is this just syntactic sugar, or is there something deeper going on here? I note that the above snippet gives the same as the following:
c = 3
d2 = dict()
d2['c'] = c # same result when printed.
Any time you are passing parameters as named params, i.e. foo(a=1, b=2) you are actually passing a dict whose keys are the parameter names (strings), and whose values are the values.
def foo(**kwargs):
print(type(kwargs))
foo(a=2, b=3)
prints
<class 'dict'>
Only strings can go as the keys to kwargs. For example, the following will not work:
foo(1=2, b=3) # fails on SyntaxError: keyword can't be an expression
foo((1,)=2, b=3) # fails on SyntaxError: keyword can't be an expression
d = {1:2, 2:4}
foo(**d) # dict unpacking works, then fails on TypeError: foo() keywords must be strings
This does work:
d = {'a': 2, 'b': 4}
foo(**d)
because the keys are strings
In your code, the dict's copy constructor is being called, on a dict created into the kwargs.

Dynamic variable in function argument

Let us say I have a function like this:
def helloWorld(**args):
for arg in args:
print(args[arg])
To call this function is easy:
helloWorld(myMsg = 'hello, world')
helloWorld(anotherMessages = 'no, no this is hard')
But the hard part is that I want to dynamically name the args from variables coming from a list or somewhere else. And I'd want for myMsg and anotherMessages to be passed from a list (for example, the hard part that I am clueless and I need help with is how to take strings into variables to be inputs of a function).
list_of_variable_names = ['myMsg','anotherMessages']
for name in list_of_variable_names:
helloWorld(name = 'ooops, this is not easy, how do I pass a variable name that is stored as a string in a list? No idea! help!')
You can create a dict using the variable and then unpack while passing it to the function:
list_of_variable_names = ['myMsg','anotherMessages']
for name in list_of_variable_names:
helloWorld(**{name: '...'})
The ** syntax is a dictionary unpacking.
So in your function, args (which is usually kwargs) is a dictionary.
Therefore, you need to pass an unpacked dictionary to it, which is what is done when f(a=1, b=2) is called.
For instance:
kwargs = {'myMsg': "hello, world", 'anotherMessages': "no, no this is hard"}
helloWorld(**kwargs)
Then, you will get kwargs as a dictionary.
def f(**kwargs):
for k, v in kwargs.items():
print(k, v)
>>> kwargs = {'a': 1, 'b': 2}
>>> f(**kwargs)
a 1
b 2
If you want to do so, you can call the function once for every name as well, by creating a dictionary on the fly and unpacking it, as Moses suggested.
def f(**kwargs):
print("call to f")
for k, v in kwargs.items():
print(k, v)
>>> for k, v in {'a': 1, 'b': 2}:
... kwargs = {k: v}
... f(**kwargs)
...
call to f
a 1
call to f
b 2

Initialize default keyword args list in python

How can one define the initial contents of the keyword arguments dictionary? Here's an ugly solution:
def f(**kwargs):
ps = {'c': 2}
ps.update(kwargs)
print(str(ps))
Expected behaviour:
f(a=1, b=2) => {'c': 2, 'a': 1, 'b': 2}
f(a=1, b=2, c=3) => {'c': 3, 'a': 1, 'b': 2}
Yet, I would like to do something a little bit more in the lines of:
def f(**kwargs = {'c': 2}):
print(str(kwargs))
Or even:
def f(c=2, **kwargs):
print(str(???))
Ideas?
First to address the issues with your current solution:
def f(**kwargs):
ps = {'c': 2}
ps.update(kwargs)
print(str(ps))
This creates a new dictionary and then has to take the time to update it with all the values from kwargs. If kwargs is large that can be fairly inefficient and as you pointed out is a bit ugly.
Obviously your second isn't valid.
As for the third option, an implementation of that was already given by Austin Hastings
If you are using kwargs and not keyword arguments for default values there's probably a reason (or at least there should be; for example an interface that defines c without explicitly requiring a and b might not be desired even though the implementation may require a value for c).
A simple implementation would take advantage of dict.setdefault to update the values of kwargs if and only if the key is not already present:
def f(**kwargs):
kwargs.setdefault('c', 2)
print(str(kwargs))
Now as mentioned by the OP in a previous comment, the list of default values may be quite long, in that case you can have a loop set the default values:
def f(**kwargs):
defaults = {
'c': 2,
...
}
for k, v in defaults.items():
kwargs.setdefault(k, v)
print(str(kwargs))
A couple of performance issues here as well. First the defaults dict literal gets created on every call of the function. This can be improved upon by moving the defaults outside of the function like so:
DEFAULTS = {
'c': 2,
...
}
def f(**kwargs):
for k, v in DEFAULTS.items():
kwargs.setdefault(k, v)
print(str(kwargs))
Secondly in Python 2, dict.items returns a copy of the (key, value) pairs so instead dict.iteritems or dict.viewitems allows you to iterate over the contents and thus is more efficient. In Python 3, 'dict.items` is a view so there's no issue there.
DEFAULTS = {
'c': 2,
...
}
def f(**kwargs):
for k, v in DEFAULTS.iteritems(): # Python 2 optimization
kwargs.setdefault(k, v)
print(str(kwargs))
If efficiency and compatibility are both concerns, you can use the six library for compatibility as follows:
from six import iteritems
DEFAULTS = {
'c': 2,
...
}
def f(**kwargs):
for k, v in iteritems(DEFAULTS):
kwargs.setdefault(k, v)
print(str(kwargs))
Additionally, on every iteration of the for loop, a lookup of the setdefault method of kwargs needs to be performed. If you truly have a really large number of default values a micro-optimization is to assign the method to a variable to avoid repeated lookup:
from six import iteritems
DEFAULTS = {
'c': 2,
...
}
def f(**kwargs):
setdefault = kwargs.setdefault
for k, v in iteritems(DEFAULTS):
setdefault(k, v)
print(str(kwargs))
Lastly if the number of default values is instead expected to be larger than the number of kwargs, it would likely be more efficient to update the default with the kwargs. To do this, you can't use the global default or it would update the defaults with every run of the function, so the defaults need to be moved back into the function. This would leave us with the following:
def f(**kwargs):
defaults = {
'c': 2,
...
}
defaults.update(kwargs)
print(str(defaults))
Enjoy :D
A variation possible on your first approach on Python 3.5+ is to define the default and expand the provided arguments on a single line, which also lets you replace kwargs on the same line, e.g.:
def f(**kwargs):
# Start with defaults, then expand kwargs which will overwrite defaults if
# it has them
kwargs = {'c': 2, **kwargs}
print(str(kwargs))
Another approach (that won't produce an identical string) that creates a mapping that behaves the same way using collections.ChainMap (3.3+):
from collections import ChainMap
def f(**kwargs):
# Chain overrides over defaults
# {'c': 2} could be defined outside f to avoid recreating it each call
ps = ChainMap(kwargs, {'c': 2})
print(str(ps))
Like I said, that won't produce the same string output, but unlike the other solutions, it won't become more and more costly as the number of passed keyword arguments increases (it doesn't have to copy them at all).
Have you tried:
def f(c=2, **kwargs):
kwargs['c'] = c
print(kwargs)
Update
Barring that, you can use inspect to access the code object, and get the keyword-only args from that, or even all the args:
import inspect
def f(a,b,c=2,*,d=1,**kwargs):
code_obj = inspect.currentframe().f_code
nposargs = code_obj.co_argcount
nkwargs = code_obj.co_kwonlyargcount
localvars = locals()
kwargs.update({k:localvars[k] for k in code_obj.co_varnames[:nposargs+nkwargs]})
print(kwargs)
g=f
g('a', 'b')

Python: pass more than one keyword argument?

is there possible to pass more than one keyword argument to a function in python?
foo(self, **kwarg) # Want to pass one more keyword argument here
You only need one keyword-arguments parameter; it receives any number of keyword arguments.
def foo(**kwargs):
return kwargs
>>> foo(bar=1, baz=2)
{'baz': 2, 'bar': 1}
I would write a function to do this for you
def partition_mapping(mapping, keys):
""" Return two dicts. The first one has key, value pair for any key from the keys
argument that is in mapping and the second one has the other keys from
mapping
"""
# This could be modified to take two sequences of keys and map them into two dicts
# optionally returning a third dict for the leftovers
d1 = {}
d2 = {}
keys = set(keys)
for key, value in mapping.iteritems():
if key in keys:
d1[key] = value
else:
d2[key] = value
return d1, d2
You can then use it like this
def func(**kwargs):
kwargs1, kwargs2 = partition_mapping(kwargs, ("arg1", "arg2", "arg3"))
This will get them into two separate dicts. It doesn't make any sense for python to provide this behavior as you have to manually specify which dict you want them to end up in. Another alternative is to just manually specify it in the function definition
def func(arg1=None, arg2=None, arg3=None, **kwargs):
# do stuff
Now you have one dict for the ones you don't specify and regular local variables for the ones you do want to name.
You cannot. But keyword arguments are dictionaries and when calling you can call as many keyword arguments as you like. They all will be captured in the single **kwarg. Can you explain a scenario where you would need more than one of those **kwarg in the function definition?
>>> def fun(a, **b):
... print b.keys()
... # b is dict here. You can do whatever you want.
...
...
>>> fun(10,key1=1,key2=2,key3=3)
['key3', 'key2', 'key1']
Maybe this helps. Can you clarify how the kw arguments are divided into the two dicts?
>>> def f(kw1, kw2):
... print kw1
... print kw2
...
>>> f(dict(a=1,b=2), dict(c=3,d=4))
{'a': 1, 'b': 2}
{'c': 3, 'd': 4}

Categories