Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
How to refactor code that is too reliant on **kwargs

Refactoring from an overuse of **kwargs

Compare:

def a(**kwargs):
  if 'A' not in kwargs:
    kwargs['A'] = 1
  if 'B' not in kwargs:
    kwargs['B'] = 2
  success, result = b(**kwargs)
  if success:
    return result
  else:
    logger.error(result)
    return None

def b(**kwargs):
  if 'A' not in kwargs:
    return False, 'A is a required argument'
  if 'B' not in kwargs:
    kwargs['B'] = 3
  if 'C' not in kwargs:
    kwargs['C'] = 4
  return True, do_something(kwargs['A'], kwargs['B'])

With

def a(A=1, B=2, **kwargs):
  return b(A=A, B=B, **kwargs)

def b(A, B=3, C=4, **kwargs):
  return do_something(A, B)
  • The code on top is equivalent to the code below. I think below is easier to read and maintain.
  • You can easily tell what arguments each function requires for its own use.
  • Django signals says you must always have **kwargs in the signature. This style allows that no problem. You can keep using kwargs, but make things explicit wherever possible.
  • b is defined to accept a non-keyword argument A: def b(A, ...) but callers may treat it like a keyword argument argument b(..., A=something, ...).

General refactoring ideas

  • We may shift the code from **kwargs-style to explicit-style incrementally. Changing a function from accepting **kwargs to accepting **kwargs plus several explicit keyword arguments will not change the calling function at all. (Callers may continue to call foo(**kwargs))
  • Treat the kwargs dict your function receives as read-only. Never assign to it.
  • Any data your function requires from kwargs (returns error or raises exception if not found), make into a named argument instead.
  • Any data your function uses from kwargs that is optional (default provided if not found) make into an explicit keyword argument. foo=None, bar="default value"
  • When calling functions, you can provide values to non-keyword arguments using keyword argument calling style.
  • When calling functions, write out the arguments explicitly, avoid passing **kwargs whenever possible.
  • Don't return error messages or log error messages about problems with args. Raise a custom exception or ValueError(message) and let calling functions that are concerned with UI decide if and how to present that message to the user, or log it, or stop execution.
  • Except for Django signal receivers, whenever a function accepts **kwargs but never uses it, you may remove it from the signature.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment