14

We have a lot of code in our internal codebase that calls our libraries internally - these libraries often have a lot of arguments (think matplotlib) and our code is often doing only a specific task and simply passes the **kwargs on to the next function called.

E.g.:

def our_method(dataframe, **kwargs):
    result = do_something_with_data(dataframe)
    external_module.draw(result, **kwargs)

While **kwargs prevents us from repeating all the parameters in our method declaration, it also makes it extremely opaque which arguments are valid when calling our_method - I have to know which method is called, which I often don't want to know.

What is your take on this?

15

How is your code used by developers? In other words, what exactly do they do to determine which arguments should be used and how?

  • If they rely on documentation automatically generated from your code, and the generator has no clue what to do with **kwargs, this is indeed problematic. Instead of finding the list of arguments and their meaning in the documentation, they have absolutely no information except the vague “it takes some arguments”.

    This problem may probably be solved by documenting the method manually, replacing the documentation generated automatically. This requires extra work from the implementer of the method, but remember, code (and its documentation) is read much more frequently than it's written.

  • If code is their documentation, the developers who use the method with **kwargs need two additional steps: they need not only to look at the signature of the method, but also at its actual implementation, in order to find the other method it actually calls. Then, they need to go to this other method to finally find what they were looking for.

    This doesn't involve a lot of effort, but still, the effort should be repeated, again and again. The worst part is that you can't help them by adding documentation: if you comment your method, listing the actual arguments, there is a big risk that the next version of the library your method calls will have different arguments, and your documentation will be outdated, since nobody will recall that it needs to be kept up to date.

My recommendation is to rely on **kwargs only for methods which have a reduced scope. Private methods (and by private in a context of Python, I mean methods starting by _) which are used in few places in the class are good candidates, for example. On the other hand, methods which are used by dozens of classes all over the code base are very bad candidates.

After all, it shouldn't take too much effort to rewrite the arguments of a method you call within the method you write. Hopefully, most methods don't take more than six to eight arguments, and if they do, ask yourself if you shouldn't be refactoring the code. In all cases:

  • Making arguments explicit within your method doesn't require a lot of effort,

  • You may want, later, to validate the arguments anyway (although if you rely only on this point to make arguments explicit, you violate YAGNI).

  • I really like this answer and think this is a good one. Unfortunately, lots of our code has lots of public methods using this pattern. But now I have arguments that we ought to change it (and drop matplotlib, never seen a crappier "interface"..) – Christian Sauer Aug 25 '15 at 19:46
3

If the next-level function has a __doc__, then you can just copy the __doc__ to your new function.

For example:

def a(x):
    """This function takes one parameter, x, and does nothing with it!"""
    pass

def b(**kwargs):
    a(**kwargs)

b.__doc__=a.__doc__

This could apply recursively, and could be applied by a decorator (which might be useful if you are doing this in bulk anyway). The __doc__ string could be manipulated, too, to add more to the end. This means the parameters shown would still be kwargs, but at least there is documentation in the help describing the actual parameters.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.