11 Gotchas

Robert C. Martin recommends designing functions to accept no more than 2 arguments.

Each function should “do one thing, and do it well”. If you need more than 2 arguments for the function to do its job, perhaps it is time to break that function into two separate functions, each doing its own thing.

When you have lots of little functions, 4-5 lines of code each, it is easy to base future code on those already available functions.

Too often I find I need to repeat a transformation that is already done at some other place.

Before that, however, I need to switch from my current task to extracting that transformation into a separate function.

Extracting is one thing, but making sure it works as expected, is another. Because it was a part of a larger method before, there usually is no isolated test going through that logic. I need to run the entire program and observe the (usually vast) output.

Whenever you find yourself adding a third (or n-th, where n>2) argument to your function, consider breaking that function into two separate functions when you are done making the logic work.

Remember: when the basic logic works. This way you’ll avoid premature optimization. This order also fits with the TDD cycle of Red -> Green -> Refactor.

In Python it is not a good idea to use mutable default parameters. This is demonstrated with the following function definition:

def foo(a=[]):
    a.append(5)
    return a

The following happens when trying to use the code:

>>> foo()
[5]
>>> foo()
[5, 5]
>>> foo()
[5, 5, 5]
>>> foo()
[5, 5, 5, 5]
>>> foo()

This behavior is discussed at https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument.

The reason for it is given in the above source as follows:

It comes simply from the fact that functions in Python are first-class objects, and not only a piece of code.

As soon as you get to think into this way, then it completely makes sense: a function is an object being evaluated on its definition; default parameters are kind of “member data” and therefore their state may change from one call to the other - exactly as in any other object.