Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Method Refactor: from many kwargs to one arg-object

Sometimes the number of kwargs of a method increase to a level where I think it should be refactored.

Example:

def foo(important=False, debug=False, dry_run=False, ...):
    ....
    sub_foo(important=imporant, debug=debug, dry_run=dry_run, ...)

My current preferred solution:

class Args(object):
    ...

def foo(args):
    sub_foo(args)

First question: How to call Args? Is there a well known description or design pattern?

Second question: Does Python have something which I could use as base class for Args?

Update

I use Python work daily since 13 years. I used methods with many kwargs and wrote methods with many kwargs. During the last weeks a read the book "clean code" and I liked it. Somehow it is like wearing an other pair of glasses now. My old code works, but it is not nice to look at. Splitting long methods into several smaller methods is easy. But I am not sure how to handle methods with kwargs-bloat.

like image 784
guettli Avatar asked Oct 01 '22 07:10

guettli


1 Answers

I think what you've described is an example of the "Context" design pattern.

I usually call your "Args" a "Context" (or a "FooContext" if it's foo-specific enough).

I think the best explanation I saw was here: http://accu.org/index.php/journals/246 ("The Encapsulate Context Pattern", by Allen Kelly in Overload Journal #63 - Oct 2004, which I saw from another SO answer: https://stackoverflow.com/a/9458244/3427357).

There's also some decent papers that elaborate further if you want an in-depth exploration: http://www.two-sdg.demon.co.uk/curbralan/papers/europlop/ContextEncapsulation.pdf https://www.dre.vanderbilt.edu/~schmidt/PDF/Context-Object-Pattern.pdf

As pointed out by yet another SO answer (https://stackoverflow.com/a/1135454/3427357), the Context pattern is considered dangerous by some (c.f. http://misko.hevery.com/2008/07/18/breaking-the-law-of-demeter-is-like-looking-for-a-needle-in-the-haystack/).

But I think the "Law of Demeter" warnings are about not over-complicating your early design more than they're about cleaning up the cruft that accidentally grew while you were solving other problems. If you're passing an "important" boolean through multiple function call layers you're already going to testing hell, and in that situation the refactor you've described is generally a pure win in my experience.

I don't think there's a standard base class for this in python, unless maybe you're lazy enough to pass an argparse.Namespace as your context object just because you already had your parameter values there.

like image 73
GrumpyOldTroll Avatar answered Oct 05 '22 06:10

GrumpyOldTroll