简体   繁体   中英

How to return a sub-function with variable arguments / exec() a definition inside another function ? (Python 3.x)

I have a foo function that returns a value that is then used as input in a bar function. In short, baz = bar(foo(•), •) .

Now, as I want to test multiple foo iterations with a bar that stays the same, I created the make_baz function in order to return baz depending on the iteration. (ie baz_i = bar(foo_i(•), •) , etc.)

The arguments and keywords are not always the same and I can not simply pass a dictionary as I am using scipy.optimize.curve_fit .

I have found a way to theoretically do what I want (see below), but it seems I can not define a function with exec inside another function (it works as expected while in global)..

def foo_1(x, arg1, arg2, kwarg1=None):
    y = "some function of x"
    return y

def foo_2(x, arg1, kwarg1=None, kwarg2=None):
    y = "some other function of x"
    return y

def bar(y, bar_arg):
    z = "function of y"
    return z

def make_baz(foo):
    args = list(inspect.signature(foo).parameters)
    args_str = ", ".join(args)
    kwargs_str = ", ".join([f"{arg}={arg}" for arg in args])
    baz = f"def baz({args_str}, bar_arg):\n" \
          f"    y = {foo.__name__}({kwargs_str})\n" \
          f"    return bar(y=y, bar_arg=bar_arg)\n"
    exec(baz)
    return baz

print(make_baz(foo_1))
print(make_baz(foo_2))

Problem: it returns strings and not actual functions.

"""
def baz(x, arg1, arg2, kwarg1, bar_arg):
    y = foo(x=x, arg1=arg1, arg2=arg2, kwarg1=kwarg1)
    return bar(y=y, bar_arg=bar_arg)
"""
"""
def baz(x, arg1, kwarg1, kwarg2, bar_arg):
    y = foo(x=x, arg1=arg1, kwarg1=kwarg1, kwarg2=kwarg2)
    return bar(y=y, bar_arg=bar_arg)
"""

Question: do you have any workaround/solution?

The solution has been found on Behavior of exec function in Python 2 and Python 3 , defining loc = locals() and adding it as a parameter for exec :

def make_baz(foo):
    args = list(inspect.signature(foo).parameters)
    args_str = ", ".join(args)
    kwargs_str = ", ".join([f"{arg}={arg}" for arg in args])
    baz = f"def baz({args_str}, bar_arg):\n" \
          f"    y = foo({kwargs_str})\n" \
          f"    return bar(y=y, bar_arg=bar_arg)\n"
    loc = locals()
    exec(baz, globals(), loc)
    return loc["baz"]

EDIT: in order for the exec to recognize bar , we have to add globals() as argument.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM