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)

      

+3


source to share


6 answers


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.

+3


source


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 )

      

+1


source


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

      

0


source


This should work

func(a=3, **{key: value for key, value in mymod.template_kvps.items() if key != 'a')})

      

However, I would suggest writing one more line of code rather than configuring it.

0


source


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)

      

0


source


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)

      

0


source







All Articles