I'm dreaming of a Python method with explicit keyword args:
def func(a=None, b=None, c=None):
for arg, val in magic_arg_dict.items(): # Where do I get the magic?
print '%s: %s' % (arg, val)
I want to get a dictionary of only those arguments the caller actually passed into the method, just like **kwargs
, but I don't want the caller to be able to pass any old random args, unlike **kwargs
.
>>> func(b=2)
b: 2
>>> func(a=3, c=5)
a: 3
c: 5
So: is there such an incantation? In my case, I happen to be able to compare each argument against its default to find the ones that are different, but this is kind of inelegant and gets tedious when you have nine arguments. For bonus points, provide an incantation that can tell me even when the caller passes in a keyword argument assigned its default value:
>>> func(a=None)
a: None
Tricksy!
Edit: The (lexical) function signature has to remain intact. It's part of a public API, and the primary worth of the explicit keyword args lies in their documentary value. Just to make things interesting. :)
Here is the easiest and simplest way:
This give the output:
{'a': 2, 'c': None, 'b': 'egg'}
. The reasonargs
should be a copy of thelocals
dictionary is that dictionaries are mutable, so if you created any local variables in this functionargs
would contain all of the local variables and their values, not just the arguments.More documentation on the built-in
locals
function here.One possibility:
IOW, it's very easy to check that the passed-in names are within the acceptable set and otherwise raise whatever you'd want Python to raise (TypeError, I guess;-). Pretty easy to turn into a decorator, btw.
Another possibility:
by making a unique object
_sentinel
you remove the risk that the caller might be accidentally passingNone
(or other non-unique default values the caller could possibly pass). This is allobject()
is good for, btw: an extremely-lightweight, unique sentinel that cannot possibly be accidentally confused with any other object (when you check with theis
operator).Either solution is preferable for slightly different problems.
This is easiest accomplished with a single instance of a sentry object:
The nice thing about this approach is that, since we're using the "is" operator, the caller can pass an empty dict as the argument value, and we'll still pick up that they did not mean to pass it. We also avoid nasty decorators this way, and keep our code a little cleaner.
I was inspired by lost-theory's decorator goodness, and after playing about with it for a bit came up with this:
Which prints this:
I'm happy with this. A more flexible approach is to pass the name of the attribute you want to use to the decorator, instead of hard-coding it to 'actual_kwargs', but this is the simplest approach that illustrates the solution.
Mmm, Python is tasty.
Perhaps raise an error if they pass any *args?
It'd be simple to factor it into taking a list of varnames or a generic parsing function to use. It wouldn't be too hard to make this into a decorator (python 3.1), too:
Note: i'm not sure how well this would work around already wrapped functions or functions that have
*args
or**kwargs
already.There's probably better ways to do this, but here's my take:
and its output:
This could probably be converted to a decorator, but my decorators need work. Left as an exercise to the reader :P