Programming, Python

Keyword arguments and merging dictionaries

**kwargs can also be passed. Opening the three files, passing dictionaries for parameters, would look like this:

fileopts = ({'file':'junk.output',
             'mode':'wt'},
            {'file':'buffered.binary.junk',
             'mode':'wb',
             'buffering':1024},
            {'file':'append text',
             'mode':'at+'})
files = []
for params in fileopts:
  files.append(open(**params))

Works great.

What, though, if you wanted to run the options for fileopts[1] (the entry for file name buffered.binary.junk) with an append mode instead of an erase-and-write ‘wb’?

Easy, using a little trick with dictionaries:

outFile = open(**{**fileopts[1], **{'mode':'ab'})

Seeing stars? Take it one operation, one level at a time.

If you unpeel dictionaries with ** inside curly braces, you get a merge. This merges dictionary a with dictionary b because of how the dictionary’s constructor gets called:

{**a, **b}

If you wanted to pass that dictionary as an argument list to a function, you would do that as:

x(**{**a, **b})

That’s where those three sets of double stars come from. **a and **b pass themselves as argument lists to the dictionary constructor, which is called when you put stuff in curly braces. The two stars on the outside of the curly braces are from the **kwargs construct.

Any keys in common between the first and second dictionaries will be overwritten with the values from the second dictionary. That’s how we can replace one key/value pair in a dictionary with a merge of this type.

The above dictionary merge has the effect of adding a key/value pair for ‘mode’. Or, if ‘mode’ already exists in the first dictionary, the second dictionary overwrites it.

In Python 3.9, the vertical bar operator does this, too, so you could give your “*” a rest:

outFile = open(**{fileopts[1] | {'mode':'ab'}})