180

Let's say that a function A is required only by function B, should A be defined inside B?

Simple example. Two methods, one called from another:

def method_a(arg):
    some_data = method_b(arg)

def method_b(arg):
    return some_data

In Python we can declare def inside another def. So, if method_b is required for and called only from method_a, should I declare method_b inside method_a? like this :

def method_a(arg):
    
    def method_b(arg):
        return some_data

    some_data = method_b(arg)

Or should I avoid doing this?

Miguel Rueda
  • 478
  • 1
  • 6
  • 13
nukl
  • 10,073
  • 15
  • 42
  • 58
  • 8
    You shouldn't need to define a function inside another unless you're doing something REALLY funky. However, please elaborate on what you're trying to do, so we can provide a more helpful answer – inspectorG4dget Jan 28 '11 at 18:29
  • 6
    Do you realize that the second example is different, because you don't *call* `method_b`? (@inspector: You do need to, strictly speaking, but it's immensely useful when you get into a bit of functional programming, in particular closures). –  Jan 28 '11 at 18:56
  • 3
    @delnan: I think you meant "You *don't* need to, strictly speaking, but..." – martineau Jan 29 '11 at 11:54
  • As @delnan mentioned, this is common in the case of closures, so I don't think it qualifies as funky; however, unless closures are necessary (which I'm guessing they are not in this case), putting one function inside another doesn't seem necessary, efficient, or tidy. Unless you need closures, I would stick with the first pattern. – Trevor Dec 03 '13 at 22:33
  • 5
    The use cases for inner functions are summarized wonderfully in link: [https://realpython.com/blog/python/inner-functions-what-are-they-good-for/](https://realpython.com/blog/python/inner-functions-what-are-they-good-for/). If your use doesn't fit into any of the cases , better avoid it. –  Jun 07 '16 at 02:39
  • Does this answer your question? [How to make a chain of function decorators?](https://stackoverflow.com/questions/739654/how-to-make-a-chain-of-function-decorators) – Henry Henrinson Feb 18 '20 at 23:30

12 Answers12

136
>>> def sum(x, y):
...     def do_it():
...             return x + y
...     return do_it
... 
>>> a = sum(1, 3)
>>> a
<function do_it at 0xb772b304>
>>> a()
4

Is this what you were looking for? It's called a closure.

user225312
  • 126,773
  • 69
  • 172
  • 181
  • why not just do def sum(x,y): return x+y? – mango Jun 03 '14 at 18:53
  • 5
    @mango: This is just a toy example to convey the concept -- in actual use what `do_it()` does would presumably be a bit more complicated than what can be handled by some arithmetic in a single `return` statement. – martineau Sep 02 '14 at 13:07
  • 2
    @mango Answered your question with a slightly more useful example. http://stackoverflow.com/a/24090940/2125392 – CivFan Nov 21 '14 at 21:27
  • 31
    It doesn't answer the question. – Hunsu Jun 30 '15 at 12:45
  • 1
    Nice answer but where is the question? Not here. OP asked whether one can do the above if method_b is only used by method_a. He did not ask anything else. No closure. – Mayou36 Jul 26 '17 at 19:40
55

You don't really gain much by doing this, in fact it slows method_a down because it'll define and recompile the other function every time it's called. Given that, it would probably be better to just prefix the function name with underscore to indicate it's a private method -- i.e. _method_b.

I suppose you might want to do this if the nested function's definition varied each time for some reason, but that may indicate a flaw in your design. That said, there is a valid reason to do this to allow the nested function to use arguments that were passed to the outer function but not explicitly passed on to them, which sometimes occurs when writing function decorators, for example. It's what is being shown in the accepted answer although a decorator is not being defined or used.

Update:

Here's proof that nesting them is slower (using Python 3.6.1), although admittedly not by much in this trivial case:

setup = """
class Test(object):
    def separate(self, arg):
        some_data = self._method_b(arg)

    def _method_b(self, arg):
        return arg+1

    def nested(self, arg):

        def method_b2(self, arg):
            return arg+1

        some_data = method_b2(self, arg)

obj = Test()
"""
from timeit import Timer
print(min(Timer(stmt='obj.separate(42)', setup=setup).repeat()))  # -> 0.24479823284461724
print(min(Timer(stmt='obj.nested(42)', setup=setup).repeat()))    # -> 0.26553459700452575

Note I added some self arguments to your sample functions to make them more like real methods (although method_b2 still isn't technically a method of the Test class). Also the nested function is actually called in that version, unlike yours.

martineau
  • 119,623
  • 25
  • 170
  • 301
  • 26
    It doesn't actually fully compile the inner function every time the outer function is called, although it does have to create a function object, which does take a little time. On the other hand, the function name becomes local rather global one, so it's faster to call the function. In my trials, time-wise it's basically a wash most of the time; it may even be faster with the inner function if you call it many times. – kindall Jan 28 '11 at 21:23
  • @kindall: It doesn't appear that it's faster to use a nested function -- see test code in my updated answer -- so at least in this case what you said doesn't appear to true. – martineau Jan 29 '11 at 00:19
  • 7
    Yeah, you'll need multiple calls to the inner function. If you're calling it in a loop, or just more than a handful of times, the benefit of having a local name for the function will begin to outweigh the cost of creating the function. In my trials, this happens when you call the inner function about 3-4 times. Of course, you could get the same benefit (without nearly as much cost) by defining a local name for the function, e.g. `method_b = self._method_b` and then call `method_b` to avoid the repeated attribute lookups. (It happens that I have been doing a LOT of timing of stuff lately. :) – kindall Jan 29 '11 at 01:00
  • 3
    @kindall: Yep, that's true. I modified my timing test so it made 30 calls to the secondary function and the results turned around. I'll be deleting my answer after you've had a chance to see this reply. Thanks for the enlightenment. – martineau Jan 29 '11 at 13:17
  • 1
    No, don't delete it; I think there is good info here. I just discovered that if you're using a decorator on the inner function, the decorator gets called every time you call the outer function. Now THAT could affect your timing! – kindall Jan 29 '11 at 17:11
  • @kindall: Interesting. In a similar vain, I independently noted that adding a `setattr(Test, 'method_b2', method_b2)` right after its definition to make it a *real* method, coupled with changing the call to `self.method_b2(arg)`, made it take 2x as long to do 6 calls. – martineau Jan 29 '11 at 18:57
  • 4
    Just wanted to explain my -1 because this is an informative answer nonetheless: I gave this a -1 because it puts focus on a trivial performance difference (in most scenarios the code object creation will only take a fraction of the execution time of the function). What's important to consider in these cases is whether having an inline function can improve code readability and maintainability, which I think it often does because you don't have to search/scroll through files to find the relevant code. – Blixt Sep 04 '15 at 16:21
  • 5
    @Blixt: I think your logic is flawed (and down-vote unfair) because it's extremely unlikely that another method of the same class would be very "far" from another one in the same class even when it's not nested (and extremely unlikely to be in a different file). Also the very first sentence in my answer is "You don't really gain much by doing this" which points out it's a trivial difference. – martineau Nov 07 '15 at 16:48
  • 1
    -1 for the same reason as @Blixt stated. It is nice to know that there is *no real time difference*, but the question is whether this is a good idea or not. And "you don't really gain much by doing this" is way to general: you gain for example encapsulation. The method_a is independent of another function. The answer is not bad, don't get this wrong, but just partially answers the question (and mostly the less relevant part). – Mayou36 Jul 26 '17 at 19:44
  • 2
    @Mayou36: You're entitled to your own opinion, of course. My answer is purposefully vague because, since there's little speed difference, the only other factors to be considered are mostly subjective, such as that relative to the importance of encapsulation, readability, etc. – martineau Jul 26 '17 at 20:51
  • Well, I would say that if it is about opinion, this *is* (or implies) an answer. Would you say than that there is no best practice? Because there are a lot of things (most of all in Python, *there is preferably only one way...*) which should or should not be done because of best practices and conventions. Speed *can* be a factor, I agree, and you nicely showed that it is clearly not the main concern. But there is still the *python-specific* best-practice part missing. Don't you think this would be nice to know? – Mayou36 Jul 26 '17 at 21:51
  • 1
    Technically, questions with primarily opinion-based answers are off-topic here. Secondly, all best-practices don't have definitive answers. I'm OK with "it depends" unless someone can show a good reason to do it one way or the other. – martineau Jul 26 '17 at 22:01
30

Generally, no, do not define functions inside functions.

Unless you have a really good reason. Which you don't.

Why not?

What is a really good reason to define functions inside functions?

When what you actually want is a dingdang closure.

CivFan
  • 13,560
  • 9
  • 41
  • 58
28

A function inside of a function is commonly used for closures.

(There is a lot of contention over what exactly makes a closure a closure.)

Here's an example using the built-in sum(). It defines start once and uses it from then on:

def sum_partial(start):
    def sum_start(iterable):
        return sum(iterable, start)
    return sum_start

In use:

>>> sum_with_1 = sum_partial(1)
>>> sum_with_3 = sum_partial(3)
>>> 
>>> sum_with_1
<function sum_start at 0x7f3726e70b90>
>>> sum_with_3
<function sum_start at 0x7f3726e70c08>
>>> sum_with_1((1,2,3))
7
>>> sum_with_3((1,2,3))
9

Built-in python closure

functools.partial is an example of a closure.

From the python docs, it's roughly equivalent to:

def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*(args + fargs), **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc

(Kudos to @user225312 below for the answer. I find this example easier to figure out, and hopefully will help answer @mango's comment.)

Community
  • 1
  • 1
CivFan
  • 13,560
  • 9
  • 41
  • 58
  • -1 yes, they are commonly used as closures. Now reread the question. He basically asked if the concept he shows can be used for case b. Telling him that this is often used for case a is not a bad answer, but the wrong one for this question. He is interested whether it is a good thing to do the above for encapsulation for example. – Mayou36 Jul 26 '17 at 19:47
  • @Mayou36 To be fair, the question is rather open-ended -- rather than trying to answer every possible case, I thought it best to focus on one. Also the question is not very clear. For example, it does imply closure in the second example, even though that's likely not what was meant. – CivFan Jul 26 '17 at 20:00
  • @Mayou36 "He basically asked if the concept he shows *can* be used for case b." No, the question asks if it *should* be used. OP already knows it can be used. – CivFan Jul 26 '17 at 20:04
  • 1
    well, " if method_b is required for and called only from method_a, should I declare method_b inside method_a?" is quite clear and has nothing to do with closures, right? Yes, I agree, I used *can* in the way of *should*. But that does not has to do with closures... I am just surprised that so many answer about closures and how to use them when the OP asked a completely different question. – Mayou36 Jul 26 '17 at 21:44
  • @Mayou36 It might help to reword the question how you see it, and open up another question to address it. – CivFan Jul 26 '17 at 22:12
10

It's actually fine to declare one function inside another one. This is specially useful creating decorators.

However, as a rule of thumb, if the function is complex (more than 10 lines) it might be a better idea to declare it on the module level.

vz0
  • 32,345
  • 7
  • 44
  • 77
  • 2
    It's possible, but I agree, you need a good reason for doing it. It would be more python to use a preceding underscore for a function that was intended to be only used within your class. – chmullig Jan 28 '11 at 18:56
  • 3
    Within a class, yes, but what about only within a *function*? Encapsulation is hierarchical. – Paul Draper Apr 13 '13 at 01:39
  • @PaulDraper "Encapsulation is hierarchical" - No! Who says that? Encapsulation is a much broader principal than just some inheritance. – Mayou36 Jul 26 '17 at 19:48
8

I found this question because I wanted to pose a question why there is a performance impact if one uses nested functions. I ran tests for the following functions using Python 3.2.5 on a Windows Notebook with a Quad Core 2.5 GHz Intel i5-2530M processor

def square0(x):
    return x*x

def square1(x):
    def dummy(y):
        return y*y
    return x*x

def square2(x):
    def dummy1(y):
        return y*y
    def dummy2(y):
        return y*y
    return x*x

def square5(x):
    def dummy1(y):
        return y*y
    def dummy2(y):
        return y*y
    def dummy3(y):
        return y*y
    def dummy4(y):
        return y*y
    def dummy5(y):
        return y*y
    return x*x

I measured the following 20 times, also for square1, square2, and square5:

s=0
for i in range(10**6):
    s+=square0(i)

and got the following results

>>> 
m = mean, s = standard deviation, m0 = mean of first testcase
[m-3s,m+3s] is a 0.997 confidence interval if normal distributed

square? m     s       m/m0  [m-3s ,m+3s ]
square0 0.387 0.01515 1.000 [0.342,0.433]
square1 0.460 0.01422 1.188 [0.417,0.503]
square2 0.552 0.01803 1.425 [0.498,0.606]
square5 0.766 0.01654 1.979 [0.717,0.816]
>>> 

square0 has no nested function, square1 has one nested function, square2 has two nested functions and square5 has five nested functions. The nested functions are only declared but not called.

So if you have defined 5 nested funtions in a function that you don't call then the execution time of the function is twice of the function without a nested function. I think should be cautious when using nested functions.

The Python file for the whole test that generates this output can be found at ideone.

miracle173
  • 1,852
  • 16
  • 33
  • 5
    The comparison you do is not really useful. It's like adding dummy statements into the function and saying it's slower. Example of martineau actually uses the encapsulated functions and I don't notice any performance difference by running his example. – kon psych Feb 19 '15 at 00:24
  • -1 please reread the question. Although you may tell that it is maybe slightly slower, he asked whether it is a good idea to do that, not mainly because of performance but of general practice. – Mayou36 Jul 26 '17 at 19:50
7

So in the end it is largely a question about how smart the python implementation is or is not, particularly in the case of the inner function not being a closure but simply an in function needed helper only.

In clean understandable design having functions only where they are needed and not exposed elsewhere is good design whether they be embedded in a module, a class as a method, or inside another function or method. When done well they really improve the clarity of the code.

And when the inner function is a closure that can also help with clarity quite a bit even if that function is not returned out of the containing function for use elsewhere.

So I would say generally do use them but be aware of the possible performance hit when you actually are concerned about performance and only remove them if you do actual profiling that shows they best be removed.

Do not do premature optimization of just using "inner functions BAD" throughout all python code you write. Please.

5

It's just a principle about exposure APIs.

Using python, It's a good idea to avoid exposure API in outer space(module or class), function is a good encapsulation place.

It could be a good idea. when you ensure

  1. inner function is ONLY used by outer function.
  2. insider function has a good name to explain its purpose because the code talks.
  3. code cannot directly understand by your colleagues(or other code-reader).

Even though, Abuse this technique may cause problems and implies a design flaw.

Just from my exp, Maybe misunderstand your question.

chao787
  • 1,760
  • 3
  • 18
  • 20
1

Do something like:

def some_function():
    return some_other_function()
def some_other_function():
    return 42 

if you were to run some_function() it would then run some_other_function() and returns 42.

EDIT: I originally stated that you shouldn't define a function inside of another but it was pointed out that it is practical to do this sometimes.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
mdlp0716
  • 190
  • 1
  • 8
  • I appreciate the effort those above you have put in to their answers, but yours was direct and to the point. Nice. – c0nfus3d Jul 05 '15 at 05:13
  • 2
    Why? Why should you not do that? Isn't that great encapsulation? I am missing any arguments. -1 – Mayou36 Jul 26 '17 at 19:53
  • @Mayou36 I didn't know what encapsulation was at the time of writing my comment, nor do i really know what it is now. I just thought it's not good to do. Can you explain why it would be beneficial to define a function inside another one, instead of just defining it outside of it? – mdlp0716 Sep 14 '17 at 18:07
  • 2
    Yes, I can. You may look up the concept of encapsulation, but in short: hide the information that is not needed and only expose to the user what is needed to know. Which means, defining some_other_function outside just adds something more into the namespace which is actually tightly bound to the first function. Or think in terms of variables: why do you need local variables vs global variables? Defining all variables inside a function, if possible, is way better then using global variables for variables *only used inside this one function*. It's all about reducing complexity in the end. – Mayou36 Sep 18 '17 at 09:39
1

It's perfectly OK doing it that way, but unless you need to use a closure or return the function I'd probably put in the module level. I imagine in the second code example you mean:

...
some_data = method_b() # not some_data = method_b

otherwise, some_data will be the function.

Having it at the module level will allow other functions to use method_b() and if you're using something like Sphinx (and autodoc) for documentation, it will allow you to document method_b as well.

You also may want to consider just putting the functionality in two methods in a class if you're doing something that can be representable by an object. This contains logic well too if that's all you're looking for.

mikelikespie
  • 5,682
  • 3
  • 31
  • 36
1

You can use it to avoid defining global variables. This gives you an alternative for other designs. 3 designs presenting a solution to a problem.

A) Using functions without globals

def calculate_salary(employee, list_with_all_employees):
    x = _calculate_tax(list_with_all_employees)

    # some other calculations done to x
    pass

    y = # something 

    return y

def _calculate_tax(list_with_all_employees):
    return 1.23456 # return something

B) Using functions with globals

_list_with_all_employees = None

def calculate_salary(employee, list_with_all_employees):

    global _list_with_all_employees
    _list_with_all_employees = list_with_all_employees

    x = _calculate_tax()

    # some other calculations done to x
    pass

    y = # something

    return y

def _calculate_tax():
    return 1.23456 # return something based on the _list_with_all_employees var

C) Using functions inside another function

def calculate_salary(employee, list_with_all_employees):

    def _calculate_tax():
        return 1.23456 # return something based on the list_with_a--Lemployees var

    x = _calculate_tax()

    # some other calculations done to x
    pass
    y = # something 

    return y

Solution C) allows to use variables in the scope of the outer function without having the need to declare them in the inner function. Might be useful in some situations.

Elmex80s
  • 3,428
  • 1
  • 15
  • 23
0

Function In function python

def Greater(a,b):
    if a>b:
        return a
    return b

def Greater_new(a,b,c,d):
    return Greater(Greater(a,b),Greater(c,d))

print("Greater Number is :-",Greater_new(212,33,11,999))
zeflex
  • 1,487
  • 1
  • 14
  • 29