4

I am learning Python and am trying to figure out the best way to structure my code.

Lets say I have a long function, and want to break it up into smaller functions. In C, I would make it a 'static' function at the top level (since that is the only level of functions). I would also probably forward declare it and place it after the now-shortened function that uses it.

Now for Python. In Python, I have the option to create a nested function. Since this new "inner" function is really only a piece of the larger function broken off for readability purposes, and only used by it, it sounds like it should be a nested function, but having this function inside the parent function causes the whole function to still be very long, since no code was actually moved out of it! And especially since the functions have to be fully coded before they are called, it means the actual short function is all the way down at the end of this pseudo-long function, making readability terrible!

What is considered good practice for situations like this?

Baruch
  • 20,590
  • 28
  • 126
  • 201
  • 1
    do you want to stay functional or would you consider programming in oop? – tim Aug 28 '13 at 07:23
  • 1
    See [Defining private module functions in python](http://stackoverflow.com/questions/1547145/defining-private-module-functions-in-python). – falsetru Aug 28 '13 at 07:25
  • I think it's best to think of your Python programming as a set of namespaces and scopes. Use classes, modules and packages, as ways to bind (encapsulate) certain types of functionality and behavior (methods) to the instances of some given type (class). Nesting should be used sparingly and only for scoping or to avoid namespace collisions. – Jim Dennis Aug 28 '13 at 07:49

4 Answers4

2

As far as I know, the main advantage of inner functions in Python is that they inherit the scope of the enclosing function. So if you need access to variables in the main function's scope (eg. argument or local variable), an inner function is the way to go. Otherwise, do whatever you like and/or find most readable.

EDIT: See this answer too.

Community
  • 1
  • 1
Henrik
  • 4,254
  • 15
  • 28
2

How about placing the smaller functions in an own file and import that in your main function? You'd have something like:

def main_func():
    from impl import a, b, c

    a()
    b()
    c()

I think this approach leads to high readability: You see where the smaller functions come from in case you want to look into them, importing them is a one-liner, and the implementation of the main function is directly visible. By choosing an appropriate file name / location, you can also tell the user that these functions are not intended for use outside of main_func (you don't have real information hiding in Python anyway).

By the way: This question doesn't have one correct answer.

flyx
  • 35,506
  • 7
  • 89
  • 126
2

So what I could understand is that you have a long function like:

def long_func(blah, foo, *args):
    ...
...
my_val = long_func(foo, blah, a, b, c)

What you have done is:

def long_func(blah, foo, *args):
    def short_func1():
        ...
    def short_func2():
        ...
    ...
    short_func1()
    short_func2()
    ...
...
my_val = long_func(foo, blah, a, b, c)

You have lots more options, I'll list two:

  1. Make it into a class

    class SomeName(object):
        def __init__(self, blah, foo, *args):
            self.blah = blah
            self.foo = foo
            self.args = args
            self.result = None  # Might keep this for returning values or see (2)
    
        def short_func1(self):
            ...
        def short_func2(self):
            ...
        def run(self):  # name it as you like!
            self.short_func1()
            self.short_func2()
            return self.result  # (2) or return the last call, on you
    ...
    my_val = SomeName(foo, blah, a, b, c).run()
    
  2. Make another module and put the short_funcs into it. Just like flyx has suggested.

    def long_func(foo, blah, *args):
        from my_module import short_func1, short_func2
    
        short_func1(foo)
        short_func2(blah)
    
pradyunsg
  • 18,287
  • 11
  • 43
  • 96
0

The good practice is to keep cycomatic complexity low. This practically means breaking your long function into many smaller functions.

The complexity is measured by the number of if, while, do, for, ?:, catch, switch, case statements, and operators && and || (plus one) in the body of a constructor, method, static initializer, or instance initializer. It is a measure of the minimum number of possible paths through the source and therefore the number of required tests. Generally 1-4 is considered good, 5-7 ok, 8-10 consider re-factoring, and 11+ re-factor now !

I suggest to take this advice, coming from Sonar, a code quality analysis tool. A good way to refactor such code is using TDD. First write unit tests to cover all the execution paths of your current function. After that you can refactor with the peace of mind that the unit tests will guarantee you didn't break anything.

If on the other hand your long function is just long, but otherwise already has a low cyclomatic complexity, then I think it doesn't matter much whether the function is nested or not.

janos
  • 120,954
  • 29
  • 226
  • 236
  • 1
    You raise some interesting points, but the question isn't about when to refactor or how to assert quality. The question is only asking how to structure or lay out code in specific situations. – flornquake Aug 28 '13 at 08:08
  • @flornquake You're right. I looked behind *the symptoms* and proposed to treat *the problem*. Telling him to nest or not the function would be a pain killer, instead I gave him the cure. Sometimes, though not always, you can do much better than taking a question literally. The long function he describes is a horror story in the making, and a time bomb. The sooner he does something about it the better for him. The question to nest a function or not will evaporate in the process. – janos Aug 28 '13 at 10:32
  • It seems to me like the OP already knows when to refactor and how to split up the function. What they want to know is where to put the smaller functions that they get when they refactor. – flornquake Aug 28 '13 at 17:02