A Journey into Python: **’ing your Arguments (and Dictionaries!)

A quick note before we start – the ** in the title refers to a double splat operator, not any kind of indecent language. That being said…

Repeating function calls with the same arguments is not the best programming practice. In fact, it’s something I am constantly working with my team to avoid. Sometimes, it is hard to avoid it when you’re writing something small. Or, alternatively, your function arguments are mostly the same, with just a couple of differences.

I’ll give a direct example from some code I am working on.

try:
  time.sleep(10)
except BaseException as e:
  cleanup_config_file(path=config_path, unique_id=unique_random_id, command_length=command_length)
  raise e
  # End here if there is an error

cleanup_config_file(path=config_path, unique_id=unique_random_id, command_length=command_length)

Important Note – Yes, I know that this is more modernly and pythonically done with a with statement, but going through the trouble to build up a class in this <200 line script seems excessive.

You’ll note that I’ve already DRY’d (Don’t Repeat Yourself) up the code already by wrapping whatever is inside cleanup_config_file into a function elsewhere. Even so…those function calls are pretty excessive.

That’s where the handy, dandy double splat operator comes in… well, handily. You can use it to send a dictionary into a function as the arguments. Without further ado.

cleanup_args = { "path": config_path, "unique_id": unique_random_id, "command_length": command_length }
try: 
  time.sleep(10)
except BaseException as e:
  cleanup_config_file(**cleanup_args)
  raise e
  # End here if there is an error

cleanup_config_file(**cleanup_args)

So, if you can’t be bothered to write a class with a proper __enter__ and __exit__ methods, then this is a lovely way to simulate a with. 1

This can also be useful if you have a lot of similar arguments, but a couple of varying ones. For this example, say you have written a function that is signing your team up for a conference.

conference_sign_up(first_name="Peter", last_name="Gibbons", company="Initech, Inc", address="123 Corporate Drive", city="Anytown", country="USA")
conference_sign_up(first_name="Michael", last_name="Bolton", company="Initech, Inc", address="123 Corporate Drive", city="Anytown", country="USA")
conference_sign_up(first_name="Samir", last_name="Nagheenanajar", company="Initech, Inc", address="123 Corporate Drive", city="Anytown", country="USA")

… which is a lot of text. Happily, we can use our **’s to make this a lot simpler.

For bonus points, we will also use them to help us quickly merge dictionaries, making the code much slicker.

office_details = { "company": "Initech, Inc", "address": "123 Corporate Drive", "city": "Anytown", "country": "USA" }
conference_sign_up(**{"first_name": "Peter", "last_name": "Gibbons", **office_details})
conference_sign_up(**{"first_name": "Michael", "last_name": "Bolton", **office_details})
conference_sign_up(**{"first_name": "Samir", "last_name": "Nagheenanajar", **office_details})

And finally, for super extra credit (because we should always be refactoring, let’s throw the repeatable details into a structure we can iterate on.

office_members = [["Peter", "Gibbons"], ["Michael", "Bolton"], ["Samir", "Nagheenanajar"]]
office_details = { "company": "Initech, Inc", "address": "123 Corporate Drive", "city": "Anytown", "country": "USA" }

for person in office_members:
  conference_sign_up(**{"first_name": person[0], "last_name": person[1], **office_details})

Hope this helps everyone write cleaner, unclean code. Happy coding!

As referenced by the only footnote above, I just want to direct anyone who wants to learn more about the with operator to this article on effbot.org.