This post originated from an RSS feed registered with Agile Buzz
by Travis Swicegood.
Original Post: Python Patterns: kwargs helper method
Feed Title: Travis Swicegood
Feed URL: http://travisswicegood.com/atom/
Feed Description: Posts on Git from Travis Swicegood, author of Pragmatic Version Control using Git.
Writing usable, functioning code can be hard enough. Now imagine writing code
that you need to make extensible enough that other developers can extend without
simply copy-n-pasting your source code and making their own modifications. That
can be rough. There are some patterns that you occasionally find in frameworks
like Django, however, that I haven’t seen documented. This morning, I
contributed a bugfix to werkzeug based on a pattern I’ve seen before.
I’m calling it the kwargs helper method.
Motivation
You have a method that returns an object or the result of a function, both of
which are variable. Through other parts of your code, other developers can
change what your function will return. Examples of this include Django’s
ListView.get_queryset, and Werkzeug’s Rule.empty (as of v0.10).
You need to allow other developers to control what gets passed into the objects
and functions as they’re called. Without such a mechanism, developers are
forced to override the entire method and in the worse case re-implement part of
your code. I want you to stop that.
Example of Problem Code
Here’s a contrived example of the code in question.
Note the type(self) call here. That returns Sheep in this example, but
returns whatever type the subclass is. So when we create a BionicSheep like
the one below, we have a problem:
123456
classBionicSheep(Sheep):def__init__(self,turbo_legs=None,**kwargs):self.turbo_legs=turbo_legssuper(BionicSheep,self).__init__(**kwargs)# what do do about cloning?
At this point, BionicSheep is broken if you try to clone it. The clone
method won’t pass in the turbo_legs value. You now have two options:
copy-n-paste the whole clone method to remove Sheep.clone from the equation
entirely or call super to get the result of Sheep.clone, then add your own
values and duplicate the assignment in __init__. The latter option isn’t
horrible in this case, but if __init__ provided different functionality based
on that kwarg you would be forced to copy-n-paste clone and provide your own
duplicate implementation.
Implementation
The solution is to provide a helper method that provides the kwargs outside of
the actual function. I’m calling this pattern the kwargs helper method. This
provides a granular hook for other developers to change the arguments that are
provided without having to override the main method and possibly duplicate code.
You need to modify the Sheep.clone method to work like this to use this
pattern:
Now you have a nice hook for providing your own custom kwargs in subclasses and
nobody has to touch clone. Here’s an implementation of BionicSheep that
works with the new code:
I started by saying that writing functioning code is hard. Making sure your
code is extensible for every other developer to use is ten times harder. Think
not only about what each line of code in your codebase does, but also how
it’s used and extended. I promise, some developer somewhere is going to want to
change just about every line of your code. Be nice and make it easy on them.