Python: in method call arguments, overrides unpacked dict keyword argument
I am trying to use the keyword template arguments for convenience in a function call (via the dict and keyword arguments), while at the same time being able to override some of the arguments.
For example, if we start with the mymod module containing the line template_kvps = {'a': 1, 'b': 2}
I can just:
import mymod
def func(**kwargs):
pass
func(**mymod.template_kvps)
then i can access mine template_kvps
inside func()
. But I want to be able to pass a different value a
with minimal overhead.
All I can think of is to change the dictionary before calling the function:, kvps = {**template_kvps, 'a': 3}; func(**kvps)
but that's twice the number of lines, and I use this function multiple times in each about 1000 test scripts.
I would like to override func
so that I can do something like func(**mymod.template_kvps, a=3)
, but as it is, Python errors with something about duplicate parameters.
btw I'm glad to consider changing the format of the kvps template.
Note added later There are some good answers to what I asked (and I accepted what I think is the best answer to what I asked), but I think I asked badly. I suggest this is a design issue and if I want default arguments like this I think it would be better to create a wrapper method with default arguments instead of a template dictionary as
def func_template(a=1, b=2):
func(a, b)
func_template(a=3)
source to share
You can use a built-in type for this purpose dict
. It takes another dict as an argument and additional key-value pairs as keyword arguments (which take precedence over values ββin the other dict).
This way you can create an updated dictionary via dict(template_vars, a=1)
.
You can deploy this argument as a key argument func(**dict(...))
.
There is also no need to change the signature of your function, and you can update / add as many key-value pairs as you like.
source to share
I would look at decorator functions as this syntax is basically the same as query. The implementation will look something like this:
def combine(func):
def wrapper(*args, **kwargs):
fargs = {}
if args and isinstance( args[0], dict ):
fargs = args[0].copy()
fargs.update(kwargs)
return func(**fargs)
return wrapper
@combine
def funky(**kwargs):
for k,v in kwargs.iteritems():
print "%s: %s" % (k, v)
# All of these work as expected
funky( {'a': 1, 'b': 2}, a=3 )
funky( {'a': 1, 'b': 2} )
funky( a=3 )
source to share
You can have a function that updates whatever values ββyou want to change. (Edited so that the original dictionary is not altered.)
import mymod
def replace(dict1, dict2):
ans = dict1.copy()
ans.update(dict2)
return ans
def func(**kwargs):
pass
func(replace(mymod.template_kvps, {'a':3}))
source to share
You can do it in one line like this:
func(**{**mymod.template_kvps, 'a': 3})
But this may not be obvious at first glance, but it is as obvious as what you have done before.
I would assume you have multiple templates (for example template_kvps_without_a
), but that will depend on your specific use case:
func(**mymod.template_kvps_without_a, a=3)
source to share
I would change the template argument as a positional argument and the keys would be overwritten as **kwargs
.
def func(template_dict, **kwargs):
updated_dict = {**template_dict, **kwargs}
return updated_dict
print(func({'a':1, 'b':2}, a=3)) # {'a': 3, 'b': 2}
Note that the double unpacking syntax requires Python 3.5+. In older versions, use:
updated_dict = template_dict.copy() updated_dict.update(kwargs)
source to share