JE_Muc
JE_Muc

Reputation: 5774

Closure after function definition

Is it possible to define a closure for a function which is already defined? For example I'd like to have a "raw" function and a function which already has some predefined values set by a surrounding closure.
Here is some code showing what I can do with a closure to add predefined variables to a function definition:

def outer(a, b, c):
    def fun(d):
        print(a + b + c - d)
    return fun

foo = outer(4, 5, 6)
foo(10)

Now I want to have a definition of fun outside of a wrapping closure function, to be able to call fun either with variables from a closure or by passing variables directly. I know that I need to redefine a function to make it usable in a closure, thus I tried using lambda for it:

def fun(a, b, c, d):  # raw function
    print(a + b + c - d)

def clsr(func):  # make a "closure" decorator
    def wrap(*args):
        return lambda *args: func(*args)
    return wrap

foo = clsr(fun)(5, 6, 7)  # make a closure with values already defined
foo(10)  # raises TypeError: fun() missing 3 required positional arguments: 'a', 'b', and 'c'
fun(5, 6, 7, 10)  # prints 8

What I also tried is using wraps from functools, but I was not able to make it work.

But is this even possible? And if yes: Is there any module which already implements decorators for this?

Upvotes: 0

Views: 48

Answers (1)

kabanus
kabanus

Reputation: 25895

You can just define the wrap on the fly:

def fun(a, b, c, d):  # raw function
    print(a + b + c - d)

def closed(d): fun(5,6,7,d)

closed(10)

You can use this with lambda, but @juanpa points out you should not if there is no reason to. The above code will result in 8. This method by the way is not Python specific, most languages would support this.

But if you need a closure in a sense that it relies on the wrapper variables, than no, and there is good reason not to. This will create essentially a non-working function, that relies on wrapping. In this case using a class maybe better:

class fun:
    def __init__(self,*args): #Can use specific things, not just *args.
        self.args = args #Or meaningful names
    def __call__(self,a, b, c, d): # raw function
        print(a + b + c - d,self.args)

def closed(d): 
    fun("some",3,"more",['args'])(5,6,7,d)

closed(10)

or using *args/**kwargs directly and passing extra variables through that. Otherwise I am not familiar with a "inner function" construct that only works after wrapping.

Upvotes: 2

Related Questions