Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically define functions with varying signature

Tags:

python

dynamic

What I want to accomplish:

dct = {'foo':0, 'bar':1, 'baz':2}
def func(**dct):
    pass
#function signature is now func(foo=0, bar=1, baz=2)

However, the ** syntax is obviously clashing here between expanding a dict (what I want to do) and declaring a parameter that holds the keyword arguments (what I don't want to do).

Is this possible?

like image 260
Lanaru Avatar asked Aug 14 '12 14:08

Lanaru


1 Answers

Based on my interpretation of your requirements -- you want to dynamically define a function with a signature that matches the content of adict provided at runtime -- there are two issues here which makes it impractical.

  1. If the arguments are defined at run-time, how can your function reference the variables? Are you planning to build the function body at run-time as well?
  2. dicts are unordered, so you cannot reliably use them to define positional arguments

I suspect this is an XY problem. If you can explain what you're trying to achieve then perhaps we can be of better help.

However, assuming you're trying to assign default keyword arguments using a dict then one way to achieve this would be to use decorators. For example:

def defaultArgs(default_kw):
    "decorator to assign default kwargs"
    def wrap(f):
        def wrapped_f(**kwargs):
            kw = {}
            kw.update(default_kw)  # apply defaults
            kw.update(kwargs)  # apply from input args
            f(**kw)  # run actual function with updated kwargs
        return wrapped_f
    return wrap

You can then use this decorator to assign default keyword arguments to a function that expects only keyword arguments:

defaults = {'foo':0, 'bar':1, 'baz':2}

@defaultArgs(defaults)
def func(**kwargs):
    print kwargs  # args accessible via the kwargs dict

Results:

func()  # prints {'baz': 2, 'foo': 0, 'bar': 1}
func(foo=2)  # prints {'baz': 2, 'foo': 2, 'bar': 1}

params = {'bar':1000, 'hello':'world'}
func(**params)  # prints {'baz': 2, 'foo': 0, 'bar': 1000, 'hello': 'world'}

Note that you will not be able to use positional arguments:

func(1, 2, 3)   # raises TypeError
like image 139
Shawn Chin Avatar answered Oct 01 '22 21:10

Shawn Chin