views:

341

answers:

8

I asked previously how the nested functions work, but unfortunately I still don't quite get it. To understand it better, can someone please show some real-wold, practical usage examples of nested functions?

Many thanks

+3  A: 

Decorators are a very popular use for nested functions. Here's an example of a decorator that prints a statement before and after any call to the decorated function.

def entryExit(f):
    def new_f():
        print "Entering", f.__name__
        f()
        print "Exited", f.__name__
    return new_f

@entryExit
def func1():
    print "inside func1()"

@entryExit
def func2():
    print "inside func2()"

func1()
func2()
print func1.__name__
Mr Fooz
Dammit! You beat me :P
Skilldrick
A: 

Python Decorators

This is actually another topic to learn, but if you look at the stuff on 'Using Functions as Decorators', you'll see some examples of nested functions.

Skilldrick
+1  A: 

Nested functions avoid cluttering other parts of the program with other functions and variables that only make sense locally.

A function that return Fibonacci numbers could be defined as follows:

>>> def fib(n):
        def rec():
            return fib(n-1) + fib(n-2)

        if n == 0:
            return 0
        elif n == 1:
            return 1
        else:
            return rec()

>>> map(fib, range(10))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

EDIT: In practice, generators would be a better solution for this, but the example shows how to take advantage of nested functions.

jbochi
It's probably worth noting that every time fib gets called, a new rec function object is created. In code cleanup cases, this can often be undesirable. For things like decorators, it's basically necessary.
Mr Fooz
+1  A: 

I have only had to use nested functions when creating decorators. A nested function is basically a way of adding some behavior to a function without knowing what the function is that you are adding behavior to.

from functools import wraps
from types import InstanceType



def printCall(func):
   def getArgKwargStrings(*args, **kwargs):
      argsString = "".join(["%s, " % (arg) for arg in args])
      kwargsString = "".join(["%s=%s, " % (key, value) for key, value in kwargs.items()])
      if not len(kwargs):
         if len(argsString):
            argsString = argsString[:-2]
      else:
         kwargsString = kwargsString[:-2]
      return argsString, kwargsString

   @wraps(func)
   def wrapper(*args, **kwargs):
      ret = None
      if args and isinstance(args[0], InstanceType) and getattr(args[0], func.__name__, None):
         instance, args = args[0], args[1:]
         argsString, kwargsString = getArgKwargStrings(*args, **kwargs)
         ret = func(instance, *args, **kwargs)
         print "Called %s.%s(%s%s)" % (instance.__class__.__name__, func.__name__, argsString, kwargsString)
         print "Returned %s" % str(ret)
      else:
         argsString, kwargsString = getArgKwargStrings(*args, **kwargs)
         ret = func(*args, **kwargs)
         print "Called %s(%s%s)" % (func.__name__, argsString, kwargsString)
         print "Returned %s" % str(ret)
      return ret
   return wrapper


def sayHello(name):
   print "Hello, my name is %s" % (name)

if __name__ == "__main__":
   sayHelloAndPrintDebug = printCall(sayHello)
   name = "Nimbuz"
   sayHelloAndPrintDebug(name)

Ignore all the mumbo jumbo in the "printCall" function for right now and focus only the "sayHello" function and below. What we're doing here is we want to print out how the "sayHello" function was called everytime it is called without knowing or altering what the "sayHello" function does. So we redefine the "sayHello" function by passing it to "printCall", which returns a NEW function that does what the "sayHello" function does AND prints how the "sayHello" function was called. This is the concept of decorators.

Putting "@printCall" above the sayHello definition accomplishes the same thing:

@printCall
def sayHello(name):
   print "Hello, my name is %s" % (name)

if __name__ == "__main__":
   name = "Nimbuz"
   sayHello(name)
manifest
I realized that the "getArgKwargStrings" is also a nested function. It is nested because it is only needed from within the "printCall" function and does not need to be accessed otherwise.
manifest
As Mr Fooz has mentioned above in another example, the nested "getArgKwargStrings" function inside of "printCall" will get defined everytime the "printCall" function is called which may or may not be desirable.
manifest
+4  A: 

Your question made me curious, so I looked in some real-world code: the Python standard library. I found 67 examples of nested functions. Here are a few, with explanations.

One very simple reason to use a nested function is simply that the function you're defining doesn't need to be global, because only the enclosing function uses it. A typical example from Python's quopri.py standard library module:

def encode(input, output, quotetabs, header = 0):
    ...
    def write(s, output=output, lineEnd='\n'):
        # RFC 1521 requires that the line ending in a space or tab must have
        # that trailing character encoded.
        if s and s[-1:] in ' \t':
            output.write(s[:-1] + quote(s[-1]) + lineEnd)
        elif s == '.':
            output.write(quote(s) + lineEnd)
        else:
            output.write(s + lineEnd)

    ...  # 35 more lines of code that call write in several places

Here there was some common code within the encode function, so the author simply factored it out into a write function.


Another common use for nested functions is re.sub. Here's some code from the json/encode.py standard library module:

def encode_basestring(s):
    """Return a JSON representation of a Python string

    """
    def replace(match):
        return ESCAPE_DCT[match.group(0)]
    return '"' + ESCAPE.sub(replace, s) + '"'

Here ESCAPE is a regular expression, and ESCAPE.sub(replace, s) finds all matches of ESCAPE in s and replaces each one with replace(match).


In fact, any API, like re.sub, that accepts a function as a parameter can lead to situations where nested functions are convenient. For example, in turtle.py there's some silly demo code that does this:

    def baba(xdummy, ydummy):
        clearscreen()
        bye()

    ...
    tri.write("  Click me!", font = ("Courier", 12, "bold") )
    tri.onclick(baba, 1)

onclick expects you to pass an event-handler function, so we define one and pass it in.

Jason Orendorff
these are good examples, but they could all be written as global functions without any loss of functionality. nested functions are more than syntactic sugar! you should give some examples where they are necessary
Claudiu
+1  A: 

They are useful when using functions that take other functions as input. Say you're in a function, and want to sort a list of items based on the items' value in a dict:

def f(items):
    vals = {}
    for i in items: vals[i] = random.randint(0,100)
    def key(i): return vals[i] 
    items.sort(key=key)

You can just define key right there and have it use vals, a local variable.

Another use-case is callbacks.

Claudiu
+1  A: 

OK, besides decorators: Say you had an application where you needed to sort a list of strings based on substrings which varied from time to time. Now the sorted functions takes a key= argument which is a function of one argument: the items (strings in this case) to be sorted. So how to tell this function which substrings to sort on? A closure or nested function, is perfect for this:

def sort_key_factory(start, stop):
    def sort_key(string):
        return string[start: stop]
    return sort_key

Simple eh? You can expand on this by encapsulating start and stop in a tuple or a slice object and then passing a sequence or iterable of these to the sort_key_factory.

Don O'Donnell
+1  A: 

Yet another (very simple) example. A function that returns another function. Note how the inner function (that is returned) can use variables from the outer function's scope.

def create_adder(x):
   def _adder(y):
       return x + y
   return _adder

add2 = create_adder(2)
add100 = create_adder(100)

>>> add2(50)
52
>>> add100(50)
150
codeape