94

What benefit or implications could we get with Python code like this:

class some_class(parent_class):
 def doOp(self, x, y):
 def add(x, y):
 return x + y
 return add(x, y)

I found this in an open-source project, doing something useful inside the nested function, but doing absolutely nothing outside it except calling it. (The actual code can be found here.) Why might someone code it like this? Is there some benefit or side effect for writing the code inside the nested function rather than in the outer, normal function?

Martijn Pieters
1.1m325 gold badges4.2k silver badges3.4k bronze badges
asked Oct 19, 2009 at 14:40
5
  • 4
    Is this the actual code you found, or just a simplified example you constructed? Commented Oct 19, 2009 at 17:43
  • It's a simplified example. The actual code can be found here: bazaar.launchpad.net/%7Eopenerp/openobject-server/trunk/… Commented Oct 20, 2009 at 8:03
  • 3
    Your link (which points to HEAD) is now not accurate. Try: bazaar.launchpad.net/~openerp/openobject-server/trunk/annotate/… Commented Dec 3, 2009 at 1:21
  • You're right Craig. Thank you. Commented Dec 7, 2009 at 11:32
  • 1
    IMHO this sample case of nested function doesn't add any value and it may actually cause confusions. Commented Jul 4, 2024 at 1:09

7 Answers 7

122

Normally you do it to make closures :

def make_adder(x):
 def add(y):
 return x + y
 return add
plus5 = make_adder(5)
print(plus5(12)) # prints 17

Inner functions can access variables from the enclosing scope (in this case, the local variable x). If you're not accessing any variables from the enclosing scope, they're really just ordinary functions with a different scope.

answered Oct 19, 2009 at 16:05
Sign up to request clarification or add additional context in comments.

2 Comments

For that I'd prefer partials: plus5 = functools.partial(operator.add, 5). Decorators would be a better example for closures.
Thanks, but as you can see in the snippet I posted, that's not the case here: the nested function is simply being called in the outer function.
63

Aside from function generators, where internal function creation is almost the definition of a function generator, the reason I create nested functions is to improve readability. If I have a tiny function that will only be invoked by the outer function, then I inline the definition so you don't have to skip around to determine what that function is doing. I can always move the inner method outside of the encapsulating method if I find a need to reuse the function at a later date.

Toy example:

import sys
def Foo():
 def e(s):
 sys.stderr.write('ERROR: ')
 sys.stderr.write(s)
 sys.stderr.write('\n')
 e('I regret to inform you')
 e('that a shameful thing has happened.')
 e('Thus, I must issue this desultory message')
 e('across numerous lines.')
Foo()
answered Oct 19, 2009 at 15:03

3 Comments

The only problem is that this can sometimes make it harder to unit test, because you can't access the inner function.
They look so lovely!
FWIY @Challenger5 if the inner function is analogous to private class functions they aren't unit-tested anyway. I have started doing this to make methods more readable while keeping the definition close to the method in question.
27

One potential benefit of using inner methods is that it allows you to use outer method local variables without passing them as arguments.

def helper(feature, resultBuffer):
 resultBuffer.print(feature)
 resultBuffer.printLine()
 resultBuffer.flush()
def save(item, resultBuffer):
 helper(item.description, resultBuffer)
 helper(item.size, resultBuffer)
 helper(item.type, resultBuffer)

can be written as follows, which arguably reads better

def save(item, resultBuffer):
 def helper(feature):
 resultBuffer.print(feature)
 resultBuffer.printLine()
 resultBuffer.flush()
 helper(item.description)
 helper(item.size)
 helper(item.type)
answered Nov 15, 2012 at 12:32

Comments

8

I can't image any good reason for code like that.

Maybe there was a reason for the inner function in older revisions, like other Ops.

For example, this makes slightly more sense:

class some_class(parent_class):
 def doOp(self, op, x, y):
 def add(x, y):
 return x + y
 def sub(x,y):
 return x - y
 return locals()[op](x,y)
some_class().doOp('add', 1,2)

but then the inner function should be ("private") class methods instead:

class some_class(object):
 def _add(self, x, y):
 return x + y
 def doOp(self, x, y):
 return self._add(x,y)
answered Oct 19, 2009 at 14:55

2 Comments

Yeah, maybe doOp took a string to specify which operator to use on the arguments...
@ArtOfWarfare it's preferred to simply use modules. Classes are for state.
6

The idea behind local methods is similar to local variables: don't pollute the larger name space. Obviously the benefits are limited since most languages don't also provide such functionality directly.

answered Oct 19, 2009 at 14:57

Comments

1

Are you sure the code was exactly like this? The normal reason for doing something like this is for creating a partial - a function with baked-in parameters. Calling the outer function returns a callable that needs no parameters, and so therefore can be stored and used somewhere it is impossible to pass parameters. However, the code you've posted won't do that - it calls the function immediately and returns the result, rather than the callable. It might be useful to post the actual code you saw.

answered Oct 19, 2009 at 14:59

1 Comment

I've added a link to the original code in a comment on my question. As you can see, mine is a simplified example, but it's still almost the same.
0

In Python, you can use a nested function to create a decorator like @decorator. *My answer explains more about decorators.

I created multiply_by_5() to use it as the decorator for sum() as shown below:

# (4 + 6) x 5 = 50
def multiply_by_5(func):
 def core(*args, **kwargs):
 result = func(*args, **kwargs)
 return result * 5
 return core
@multiply_by_5 # Here
def sum(num1, num2):
 return num1 + num2
result = sum(4, 6)
print(result)

Output:

50

The code below is the case of not using the decorator:

# (4 + 6) x 5 = 50
# ...
# @multiply_by_5
def sum(num1, num2):
 return num1 + num2
f1 = multiply_by_5(sum) # Here
result = f1(4, 6)
print(result)

Or:

# (4 + 6) x 5 = 50
# ...
# @multiply_by_5
def sum(num1, num2):
 return num1 + num2
result = multiply_by_5(sum)(4, 6) # Here
print(result)

Output:

50
answered Nov 24, 2022 at 16:15

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.