The Artima Developer Community
Sponsored Link

Agile Buzz Forum
Python Patterns: kwargs helper method

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Travis Swicegood

Posts: 191
Nickname: tswicegood
Registered: Dec, 2008

Travis Swicegood is AppDev @ Ning and author of Pragmatic Version Control using Git
Python Patterns: kwargs helper method Posted: Feb 7, 2015 2:36 PM
Reply to this message Reply

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.
Latest Agile Buzz Posts
Latest Agile Buzz Posts by Travis Swicegood
Latest Posts From Travis Swicegood

Advertisement

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.

1
2
3
4
5
6
class Sheep(object):
    def __init__(self, name=Dolly):
        self.name = name

    def clone(self):
        return type(self)(name=self.name)

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:

1
2
3
4
5
6
class BionicSheep(Sheep):
    def __init__(self, turbo_legs=None, **kwargs):
        self.turbo_legs = turbo_legs
        super(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:

1
2
3
4
5
6
7
8
9
class Sheep(object):
    def __init__(self, name=Dolly):
        self.name = name

    def clone(self):
        return type(self)(**self.get_clone_kwargs())

    def get_clone_kwargs(self):
        return {name: self.name}

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:

1
2
3
4
5
6
7
8
9
class BionicSheep(Sheep):
    def __init__(self, turbo_legs=None, **kwargs):
        self.turbo_legs = turbo_legs
        super(BionicSheep, self).__init__(**kwargs)

    def get_clone_kwargs(self):
        kwargs = super(BionicSheep, self).get_clone_kwargs()
        kwargs[turbo_legs] = self.turbo_legs
        return kwargs

Conclusion

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.

Read: Python Patterns: kwargs helper method

Topic: Making Agile Product Roadmaps Work Previous Topic   Next Topic Topic: Bliki: DataLake

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use