4

Below is the basic logic for function foo:

def foo(item_lst):
    val_in_foo_scope = 1        

    for item in item_lst:
        # some logic to deal with item
        # val_in_foo_scope used
        pass

    return 0

The logic in the loop can be very complex, in order to make the code more clear, I want to split the logic with a separate function.


With inner function:

def foo(item_lst):
    val_in_foo_scope = 1 

    def some_logic(item):
        # val_in_foo_scope used
        pass

    for item in item_lst:
        some_logic(item)

    return 0

With outer function:

def some_logic(item, val):
    # val used
    pass

def foo(item_lst):
    val_in_foo_scope = 1 

    for item in item_lst:
        some_logic(item, val_in_foo_scope)

    return 0

The inner function version

  1. val_in_foo_scope can be used directly -- good
  2. we can easily know that the some_logic is relevant with foo, actually only be used in function foo -- good
  3. each time function foo is called, a new inner function will be created -- not so good

The outer function version

  1. val_in_foo_scope can not be used directly -- not so good
  2. we can not see the relevance between some_logic and foo directly -- not so good
  3. some_logic will be created one time -- good
  4. there will be so many functions in the global namespace -- not so good

So, which solution is better or is there any other solutions?

Factors below or any other factors you come up with can be considered:

  1. val_in_foo_scope is used or not
  2. whether the time cost to create inner function each time can be ignored
sting_roc
  • 233
  • 2
  • 15
  • 2
    if the logic is completely dependent and used by the main function then an inner function should do, otherwise if the logic is used by multiple methods and is independent, two different methods would make things clear – Keerthana Prabhakaran Jan 02 '18 at 15:17
  • 1
    If you are sure that your inner function is suited only for the use within that function, and won't be needed to execute outside it, then it makes perfect sense to define it inside. – Moinuddin Quadri Jan 02 '18 at 15:18
  • have you tried maybe going through a class to black-box the inner logic? something like a `FooManager` with the method `FooManager.inner_logic(item_list)` it pretty much only keeps the good points in question – Mixone Jan 02 '18 at 15:18
  • you can also make the class a Callable if you take advice from [this question](https://stackoverflow.com/questions/25292239/are-functions-objects-in-python) – Mixone Jan 02 '18 at 15:20
  • 1
    Use lambda if it's a simple function, use an inner function if it is complex and you don't want to make it "public". Use a method if you want to make it "public" and uses members of the instance. Use a class method if it uses class members. And lastly use a global function if it's general enough to be used by other classes/functions. – Gábor Fekete Jan 02 '18 at 15:22
  • @GáborFekete you should post that as an answer probably, it seems to cover all use cases and you can maybe add links to documentation on each part of it – Mixone Jan 02 '18 at 15:24
  • 1
    @Mixone alright, I added it as answer and expanded it with private methods. – Gábor Fekete Jan 02 '18 at 15:34
  • 1
    @Mixone It seems that like the outer function version, using class can not show the relevance clearly compared with the inner function version? – sting_roc Jan 02 '18 at 15:35
  • @sting_roc inside the class the whole relevance issue is more to do with how python deals with "privacy". The concept of class in this case is more abstract, it is basically a function and not a "real" object. Hence if this class has a PUBLIC callable method, this is our function basically, and then any PRIVATE methods of this class as helpers to the public one are clearly blackboxed from the outside. But methods are all public so it is usual to use `_method` for a private and `method` for a public, but at the end of the day it is just aesthetic unlike in java or C++ – Mixone Jan 10 '18 at 10:37

2 Answers2

3

Use lambda if it's a simple function.

Use an inner function if it is complex and you don't want to make it "public".

Use a "private" method if you want to mark it hidden and uses members of the instance.

Use a method if you want to make it "public" and uses members of the instance.

Use a class method if it uses class members.

And lastly use a global function if it's general enough to be used by other classes/functions.

Gábor Fekete
  • 1,343
  • 8
  • 16
2

You forgot one point in your pro/cons list: testability. Keeping some_logic out of foo makes it testable in isolation, which is important if it's indeed a "complex" (hence very probably critical) function.

As a general rule, only use inner functions when you have both of those conditions: it's trivial stuff and passing the required context (the 'outer' function's context) would be a pain.

(nb: I'm of course not talking about using inner functions for closures - like in a decorator - here).

bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118