• +43 660 1453541
  • contact@germaniumhq.com

Better Closures in Python Using Regular Objects


Better Closures in Python Using Regular Objects

If you remember, a while back, I’ve run into an issue regarding creating callbacks that were bound to the local context. Namely, the garbage collector pressure, and memory usage, since these functions are invisibly linking to the previous stack frames. I found an easy pattern to break these links down, using regular Python objects.

Errata: As you can see in the following article, this approach, while more readable, performs worse performance-wise.

So just to restate, the original problem was this:

def some_function(name):
    prefixed_name = "pre-" + name

    def inner_call(postfix):
        # this creates a hidden reference to the `some_function` stack frame
        return prefixed_name + "-" + postfix

    return inner_call

The inner_call will need the data from the stack frame of the method that calls it.

To break it down, we’re going to use simple Python objects. In Python, not only functions can be called, but Callable objects. So we can have an object that gets the required context information via its init() constructor, and uses it in its call():

class InnerCall:
    def __init__(self, prefixed_name):
        self.prefixed_name = prefixed_name

    def __call__(self, postfix):
        return self.prefixed_name + "-" + postfix


def some_function(name):
    prefixed_name = "pre-" + name

    return InnerCall(prefixed_name)

This approach won’t require any stack references, and it’s usable in almost all cases where otherwise, an internal function would be needed.

Conclusion

Use callable objects, objects that implement call, instead of internal functions.