expanding dictionary to function arguments

Bengt Richter bokr at oz.net
Wed Nov 2 12:42:00 EST 2005


On 1 Nov 2005 17:17:00 -0800, "Noah" <noah at noah.org> wrote:

>I have a dictionary that I would like to expand to satisfy a
>function's agument list. I can used the ** syntax to pass a dictionary,
>but
>this only works if each key in the dictionary matches an argument.
>I cannot pass a dictionary that has more keys than the function has
>arguments.
[...]
>I created the following function to do what I am describing.
>This isn't too bad, but I thought that perhaps there was some
>secret Python syntax that will do this for me.
>
>def apply_smart (func, args):
>    """This is similar to func(**args), but this won't complain about
>    extra keys in 'args'. This ignores keys in 'args' that are
>    not required by 'func'. This passes None to arguments that are
>    not defined in 'args'. That's fine for arguments with a default
>valeue, but
>    that's a bug for required arguments. I should probably raise a
>TypeError.
>    """
Ok, so why not do it? ;-)
>    if hasattr(func,'im_func'): # Handle case when func is a class
>method.
>        func = func.im_func
         skipself = True
     else: skipself = False
>    argcount = func.func_code.co_argcount
Make arg list and call with it instead of
>    required_args = dict([(k,args.get(k)) for k in
>func.func_code.co_varnames[:argcount]])
>    return func(**required_args)
     try:
         required_args = [args[k] for k in  func.func_code.co_varnames[skipself:argcount]]
     except KeyError:
         raise TypeError, '%s(...) missing arg %r'%(func.func_name, k)
     return func(*required_args)

>
>So, I now I can do this:
>    options = read_config ("options.conf")
>    apply_smart (extract_audio, options)
>    apply_smart (compress_audio, options)
>    apply_smart (mux, options)
>
>Neat, but is that the best I can do?
>
I suppose you could replace your local bindings of extract_audio, compress_audio, and mux
with wrapper functions of the same name that could cache the func and arg names in closure
variables, e.g., using a decorator function (virtually untested)

def call_with_args_from_dict(func):
    argnames = func.func_code.co_varnames[hasattr(func, 'im_func'):func.func_code.co_argcount]
    ndefaults = len(func.func_defaults or ())
    if ndefaults:
        defnames = argnames[-ndefaults:]
        argnames = argnames[:-ndefaults]
    else:
        defnames = []
    def _f(**args):
        try:
            actualargs = [args[argname] for argname in argnames]
            for argname in defnames:
                if argname not in args: break
                actualargs.append(args[argname])             
        except KeyError: raise TypeError, '%s(...) missing arg(s) %r'%(
                             func.func_name, [argname for argname in argnames if argname not in args])
        return func(*actualargs)
    _f.func_name = func.func_name
    return _f


and then wrap like

    extract_audio = call_with_args_from_dict(extract_audio)

or use as a decorator if you are defining the function to be wrapped, e.g.,
  
    @call_with_args_from_dict
    def mux(firstarg, second, etc):
        ...

Regards,
Bengt Richter



More information about the Python-list mailing list