6

What's the approved programming pattern for distributing keyword arguments among called functions?

Consider this contrived (and buggy) example:

def create_box(**kwargs): box = Box() set_size(box, **kwargs) set_appearance(box, **kwargs) def set_size(box, width=1, height=1, length=1): ... def set_appearance(box, material='cardboard', color='brown'): ... 

Obviously the set_size() method will object to receiving material or color keyword arguments, just as set_appearance() will object to receiving width, height, or length arguments.

There's a valid argument that create_box() should make all of the keyword and defaults explicit, but the obvious implementation is rather unwieldy:

def create_box(width=1, height=1, length=1, material='cardboard', color='brown'): box = Box() set_size(box, width=width, height=height, length=length) set_appearance(box, material=material, color=color) 

Is there a more Pythonic way to approach this?

1
  • IMO the kind of runtime bugs you can get by trying to hide a long list of args inside a kwargs can often be more unwieldy than just listing out the arguments explicitly Commented Nov 12, 2018 at 15:05

3 Answers 3

5

You could add **kwargs as the last argument to the functions that would otherwise become annoyed.

def create_box(**kwargs): box = Box() set_size(box, **kwargs) set_appearance(box, **kwargs) def set_size(box, width=1, height=1, length=1, **kwargs): ... def set_appearance(box, material='cardboard', color='brown', **kwargs): ... 
Sign up to request clarification or add additional context in comments.

Comments

3

Assuming you can't alter set_size or set_appearance, you could filter kwargs depending on the expected keword arguments, I'm not sure if this is the most pythonic way though...

import inspect def create_box(**kwargs): box = None set_size_args = inspect.getargspec(set_size).args set_size(box, {k:v for k,v in kwargs.items() if k in set_size_args}) set_appearance_args = inspect.getargspec(set_appearance).args set_appearance(box, {k:v for k,v in kwargs.items() if k in set_appearance_args}) def set_size(box, width=1, height=1, length=1): pass def set_appearance(box, material='cardboard', color='brown'): pass create_box(width=1, height=1, length=1, material='', color='') 

(use inspect.signature() for Python 3)

Comments

1

Hm, personally I would pass an object containing all of the params I needed. That way you're only passing one param all along.

Example:

class BoxDefinition: def __init__(self, width=1, height=1, length=1, material='cardboard', color='brown'): self.width = width self.height = height # and so on for the rest of the params ... 

Internally in the each method you can read from this object:

def create_box(box_definition): box = Box() set_size(box, box_definition) set_appearance(box, box_definition) def set_size(box, box_definition): width = box_definition.width def set_appearance(box, box_definition): color = box_definition.color 

Then you can call it like this:

definition = BoxDefinition(width=2, height=3, length=4) create_box(definition) 

In fact, you can even make BoxDefinition and attribute of the Box class, and then the only thing you'd have to pass is Box.

2 Comments

This can get unwieldy if you have functions sharing the same kwarg but with different default values.
Fair point, I figured in the OPs scenario this would not be the case as the sets of kwargs are unique among functions

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.