Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > expanding dictionary to function arguments

Reply
Thread Tools

expanding dictionary to function arguments

 
 
Noah
Guest
Posts: n/a
 
      11-02-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

 
Reply With Quote
 
 
 
 
Bruno Desthuilliers
Guest
Posts: n/a
 
      11-02-2005
Noah a écrit :
> 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.


If you have control over the API functions declarations, makes them so:

def my_api_func(arg1='', arg2='whatever', **kwargs):
code_here

 
Reply With Quote
 
 
 
 
Noah
Guest
Posts: n/a
 
      11-02-2005

Bruno Desthuilliers a écrit :
> Noah a écrit :
> If you have control over the API functions declarations, makes them so:
> def my_api_func(arg1='', arg2='whatever', **kwargs):
> code_here


Unfortunately I cannot change the API functions.
I should have mentioned that.

Yours,
Noah

 
Reply With Quote
 
bruno at modulix
Guest
Posts: n/a
 
      11-02-2005
Noah wrote:
> Bruno Desthuilliers a écrit :
>
>>Noah a écrit :
>>If you have control over the API functions declarations, makes them so:
>>def my_api_func(arg1='', arg2='whatever', **kwargs):
>> code_here

>
>
> Unfortunately I cannot change the API functions.
> I should have mentioned that.


Yeps... That what I thought, but you didn't mention it, and it was not
that clear (or it's me being stupid...).

I'm no great expert, but apart from writing a generic decorator for the
API objects and decorating them all with your smart_apply function -
which won't by you much in this case imho -, I don't see a much better
solution...

Some guru around ???

--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in '(E-Mail Removed)'.split('@')])"
 
Reply With Quote
 
Bengt Richter
Guest
Posts: n/a
 
      11-02-2005
On 1 Nov 2005 17:17:00 -0800, "Noah" <(E-Mail Removed)> 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
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Call again a variadic function (... variable number of arguments)with same arguments that its variadic wrapper moreau.steve@gmail.com C Programming 3 12-31-2008 07:13 AM
Expanding array as function arguments Witold Rugowski Ruby 5 12-05-2007 02:45 PM
how to pass a function name and its arguments inside the arguments of other function? jmborr Python 1 11-03-2007 08:20 AM
function default arguments from other arguments tutmann C++ 4 10-17-2006 08:00 PM
function call with arguments which takes no arguments Neo C Programming 10 01-20-2005 06:31 AM



Advertisments