Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

Sometimes I find myself wanting to run the same code from a few different spots in the same function. Say I have some function func1, and I want to do the same thing from a few different spots in func1. Normally the way to do this would be to write another function, call it "func2", and call func2 from several different places in func1. But what about when it's convenient to have func2 access variables that are local to func1? I find myself writing a closure. Here's a contrived example:

import random
import string

def func1 (param1, param2):
    def func2(foo, bar):
        print "{0} {1} {2:0.2f} {3} {4} {0}".format('*'*a, b, c, foo, bar)

    a = random.randrange(10)
    b = ''.join(random.choice(string.letters) for i in xrange(10))
    c = random.gauss(0, 1)
    if param1:
        func2(a*c, param1)
    else:
        if param2 > 0:
            func2(param2, param2)

Is this the Pythonic way to handle this problem? A closure feels like pretty heavy machinery to be rolling out here, especially given that I have to construct a new function every time func1 is called, even though that function is going to be basically the same every time. But it does avoid the duplicated code and in practice the overhead of repeatedly creating func2 doesn't matter to me.

share|improve this question
2  
I find your idea OK, although I would define the variables that are captured in the closure before the closure itself (it makes the code more readable IMO). I am not sure if this is Pythonic but I do not see any reason why it shouldn't be. –  Giorgio Apr 22 at 23:49
add comment

2 Answers 2

It is a an acceptable form. As @Giorgio said, I would put the closure after the captured variable definition to ease the flow of reading.

The alternative form would be to define another function, taking a, b, c as parameters. That is 5 parameters which is a lot. The closure allows you avoid repeating yourself in a very simple way. This is a big win for your version.

You can use the timeit module to compare the performances of simple snippets. You should check yourself that a closure is not a heavy machinery. The only problem I see is that it creates more nested elements. So if you find yourself writing a big closure, you should try to extract the complex part outside. But in this case I don't think it is an issue.

import timeit
import random
import string

def func1 (param1, param2):
    def func2(foo, bar):
        return "{0} {1} {2:0.2f} {3} {4} {0}".format('*'*a, b, c, foo, bar)

    a = random.randrange(10)
    b = ''.join(random.choice(string.letters) for i in xrange(10))
    c = random.gauss(0, 1)
    if param1:
        func2(a*c, param1)
    else:
        if param2 > 0:
            func2(param2, param2)

def func4(foo, bar, a, b, c):
    return "{0} {1} {2:0.2f} {3} {4} {0}".format('*'*a, b, c, foo, bar)

def func3 (param1, param2):

    a = random.randrange(10)
    b = ''.join(random.choice(string.letters) for i in xrange(10))
    c = random.gauss(0, 1)
    if param1:
        func4(a*c, param1, a, b, c)
    else:
        if param2 > 0:
            func4(param2, param2, a, b, c)

print timeit.timeit('func1("tets", "")',
 number=100000,
 setup="from __main__ import func1")

print timeit.timeit('func3("tets", "")',
 number=100000,
 setup="from __main__ import func3")
share|improve this answer
add comment

TL;DR: Create function (ideally at import time, not execution time) unless you need to save state

A closure is so named because you "close over" a lexical variable in the enclosing scope. This allows you to save state. That is why you use closures.

If a, b, and c represent variables declared in func1 you have a closure. If they do not represent captured variables from the enclosing scope the you just have a function declared inside a function.

If you want to capture values, but aren't going to modify them I would use functools.partial instead of a closure or a dynamically defined function. ie.

from functools import partial

def func1(a, b):    
  print a, b

def func2(i, j ,k):
  fn = partial(func1, i)
  fn(j)
  fn(k)

I would still make func1 a regular helper function declared outside of func2. Then possibly use partial to save on passing arguments on subsequent calls.

share|improve this answer
1  
I do not agree that, in general, closures are only useful to capture state (even though this is a good application for them). Closures can be used to capture any name that is defined in the enclosing lexical scope: this simplifies code because you do not need to pass locally-defined symbols as arguments. –  Giorgio Apr 23 at 9:12
    
@Giorgio: partial application is better suited to the task of fixing values so you don't have to repeatedly pass them on subsequent calls. Why incur the additional complexity of you don't have to? –  dietbuddha Apr 24 at 15:00
    
I am not criticizing your example but the general statement that you (only) use closures to save state. Consider for example that closures are widely used in Haskell, even though they are not used to represent state there. –  Giorgio Apr 25 at 7:59
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.