0

Given a list with function names, add them as functions to a module so that they can be invoked from the module using the function name as an argument.

from package import module_name

Functions = [ 'Add', 'Subtract' ]

for function in Functions:
  def api_endpoint(*args):
    return module_name.api(function, *args)

  setattr(module_name, function, api_endpoint)

I want to be able to call module_name.Add(arg1, arg2) and invoke module_name.api('Add', arg1, arg2).

How can I pass on this sort of introspection and dynamically add a function to a module? I tried setting it up this way, but any invocation of module_name.Add always invokes the last item in Functions (e.g. Subtract), which is incorrect.

I tried making api_endpoint a Class which overrides __new__, but that did not help, since if __new__ does not return an instance of the class being created, __init__ is never invoked and I can't get instance variables.

1 Answers1

0

Using functools.partial:

from functools import partial
from TestPackage import TestModule

Functions = [ 'Add', 'Subtract' ]

for function in Functions:
    p = partial(TestModule.api, function)
    setattr(TestModule, function, p)


TestModule.Add(1,2)         # Add 1 2
TestModule.Subtract(3,4)    # Subtract 3 4

Where TestModule contains simply:

def api(func, *args):
    print(func, *args)

Or, leveraging how arguments are evaluated:

from TestPackage import TestModule

functions = [ 'Add', 'Subtract' ]

for fn in functions:
    def api_endpoint(*args, fn=fn):               # Added 'fn=fn'
        return TestModule.api(fn, *args)

    setattr(TestModule, fn, api_endpoint)


TestModule.Add(1,2)         # Add 1 2
TestModule.Subtract(3,4)    # Subtract 3 4

Or, using closures (not a huge fan of this, especially compared to the first):

from TestPackage import TestModule

functions = [ 'Add', 'Subtract' ]

for fn in functions:
    def bind_fn(fn):
        def api_endpoint(*args):
            return TestModule.api(fn, *args)

        return api_endpoint

    setattr(TestModule, fn, bind_fn(fn))


TestModule.Add(1,2)         # Add 1 2
TestModule.Subtract(3,4)    # Subtract 3 4

Edit: The reason your approach was using "Subtract" as the func being passed (for both invocations) is explained here.

jedwards
  • 29,432
  • 3
  • 65
  • 92
  • I really like the use of the default argument evaluation. I don't have to import anything else. Why does this work, but passing the function its self doesn't? – SRichardson Jun 16 '18 at 05:53
  • In your case, when you call `TestModule.Add`, it calls `api_endpoint` which ***then*** looks up `fn` in the scope it was defined in. But `fn` has since changed. I added a third option to the answer which illustrates how you can use a closure to "localize" the lookup. In the third option, `fn` is still looked up, but now looked up in the local scope of the `bind_fn` function and that *doesn't* change during the loop. The second option is similar, in that default arguments are evaluated once, when the function definition is evaluated. So `fn` is looked up locally, inside `api_endpoint`. – jedwards Jun 16 '18 at 05:56