Automatic binding of **kwargs to variables

Alex Martelli aleaxit at yahoo.com
Sun Oct 30 16:35:14 CET 2005


lbolognini at gmail.com <lbolognini at gmail.com> wrote:

> Alex Martelli wrote:
> 
> > I find this style of coding repulsive when compared to:
> >
> > def foo(arg1=None, arg2=None):
> >     print dict(arg1=arg1, arg2=arg2)
> >
> > I don't understand what added value all of those extra, contorted lines
> > are supposed to bring to the party.
> 
> Hi Alex,
> 
> the thing is that I have about 30 parameters to pass to a method and I
> wanted to have a cleaner way to deal with it than just declaring them
> all as arguments: hence **kwargs and all that mess ;-)

Aha, I see -- yes, a nasty case; I understand why you don't want the
boilerplate of 30 'optional named arguments'.


> I was thinking to declare the a list of valid and expected parameters
> in a module and then import * and run my checks against it. Since this

A list seems inappropriate, because it implies an order you don't care
about; I would suggest a set instead.  Also, I don't see why "import *"
would be better than a normal import here -- the latter seems better.

> stuff is being posted by a web form I also need to deal with checkbox
> not being posted if not checked which lead me to:
> 
> for name in expected_form1_kwargs:
>     if name not in kwargs:
>         kwargs[name]=None

Set operations appear to be at a more appropriate level of abstraction
for what you're doing, it seems to me.  You want to ensure that kwargs,
seen as a set, is a subset of expected_kwargs, and then enrich kwargs
with None for all names that are in expected_kwargs but not yet in
kwargs; the most direct expression of this (assuming you want a detailed
diagnostics of wrongly-passed argument names), it appears to me, is to
keep in expected_args the dictionary mapping each expected arg to its
default value (dict.fromkeys(listofnames), if listofnames is a list of
the expected arguments' names a None the default value for each) and
then check in your method with code such as:

spurious_args = set(kwargs).difference(expected_args)
if spurious_args:
    raise TypeError, 'spurious args %s' % spurious_args
kwargs = dict(expected_args, **kwargs)

If you didn't need the list of spurious argument names for diagnostic
purposes, the check might be more simply expressed as

if not set(kwargs).issubset(expected_args): ...

which is more readable (IMHO the difference isn't quite enough to
warrant checking in this way and then computing the spurious_args only
if needed, i.e., in the body of the if statement, but that's a debatable
issue of style).


Operating on sets, and more generally at higher abstraction levels, is
often a preferable Python alternative to "detailed looping" at a level
that's closer to the concrete data structures in your code.


Alex



More information about the Python-list mailing list