expanding dictionary to function arguments

Noah noah at noah.org
Tue Nov 1 20:17:00 EST 2005


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.

# Example 1 - This works:
# Prints "hello world!"
def foo (arg1='greetings', arg2='planet', arg3='.'):
    print arg1 + ' ' + arg2 + arg3
args = {'arg1':'hello', 'arg2':'world', 'arg3':'!'}
foo (**args)

# Example 2 - This does not work:
# raises TypeError: foo() got an unexpected keyword argument 'arg4')
def foo (arg1='greetings', arg2='planet', arg3='.'):
    print arg1 + ' ' + arg2 + arg3
args = {'arg1':'hello', 'arg2':'world', 'arg3':'!', 'arg4':'ignore'}
foo (**args)

As a practical application, I have a project where I have a config file

that defines a large number of keys and values. I read the config
file into a dictionary called "options". I also have an API module with
many
functions that I want to call with arguments taken directly from the
"options" dictionary. The key names in the "options" dictionary match
the argument names  of the functions in my API.

# The ugly, brutish way:
    options = read_config ("options.conf")
    extract_audio (options['source_video_filename'])
    compress_audio (options['audio_raw_filename'],
options['audio_compressed_filename'], options['audio_sample_rate'],
options['audio_bitrate'])
    mux (options['source_video_filename'],
options['audio_compressed_filename'], options['output_video_filename'])

I know that the keys in my "options" dictionary match the arguments
of the functions in the API library, so I would like to do this:
    options = read_config ("options.conf")
    extract_audio (**options)
    compress_audio (**options)
    mux (**options)

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.
    """
    if hasattr(func,'im_func'): # Handle case when func is a class
method.
        func = func.im_func
    argcount = func.func_code.co_argcount
    required_args = dict([(k,args.get(k)) for k in
func.func_code.co_varnames[:argcount]])
    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?

Yours,
Noah




More information about the Python-list mailing list