Casebash Casebash - 7 months ago 18
Python Question

Set function signature in Python

Suppose I have a generic function f. I want to programmatically create a function f2 that behaves the same as f, but has a customised signature.

More detail

Given a list l and and dictionary d I want to be able to:

  • Set the non-keyword arguments of f2 to the strings in l

  • Set the keyword arguments of f2 to the keys in d and the default values to the values of d

ie. Suppose we have

l=["x", "y"]

def f(*args, **kwargs):
#My code

Then I would want a function with signature:

def f2(x, y, opt=None):
#My code

A specific use case

This is just a simplified version of my specific use case. I am giving this as an example only.

My actual use case (simplified) is as follows. We have a generic initiation function:

def generic_init(self,*args,**kwargs):
"""Function to initiate a generic object"""
for name, arg in zip(self.__init_args__,args):
setattr(self, name, arg)
for name, default in self.__init_kw_args__.items():
if name in kwargs:
setattr(self, name, kwargs[name])
setattr(self, name, default)

We want to use this function in a number of classes. In particular, we want to create a function init that behaves like generic_init, but has the signature defined by some class variables at creation time:

class my_class:
__init_args__=["x", "y"]
__kw_init_args__={"my_opt": None}

__init__=create_initiation_function(my_class, generic_init)
setattr(myclass, "__init__", __init__)

We want create_initiation_function to create a new function with the signature defined using init_args and kw_init_args. Is it possible to writ create_initiation_function?

Please note:

  • If I just wanted to improve the help, I could set doc.

  • We want to set the function signature on creation. After that, it doesn't need to be changed.

  • Instead of creating a function like generic_init, but with a different signature we could create a new function with the desired signature that just calls generic_init

  • We want to define create_initiation_function. We don't want to manually specify the new function!



For your usecase, having a docstring in the class/function should work -- that will show up in help() okay, and can be set programmatically (func.__doc__ = "stuff").

I can't see any way of setting the actual signature. I would have thought the functools module would have done it if it was doable, but it doesn't, at least in py2.5 and py2.6.

You can also raise a TypeError exception if you get bad input.

Hmm, if you don't mind being truly vile, you can use compile()/eval() to do it. If your desired signature is specified by arglist=["foo","bar","baz"], and your actual function is f(*args, **kwargs), you can manage:

argstr = ", ".join(arglist)
fakefunc = "def func(%s):\n    return real_func(%s)\n" % (argstr, argstr)
fakefunc_code = compile(fakefunc, "fakesource", "exec")
fakeglobals = {}
eval(fakefunc_code, {"real_func": f}, fakeglobals)
f_with_good_sig = fakeglobals["func"]

help(f)               # f(*args, **kwargs)
help(f_with_good_sig) # func(foo, bar, baz)

Changing the docstring and func_name should get you a complete solution. But, uh, eww...