Getting the keyword arguments actually passed to a Python method Getting the keyword arguments actually passed to a Python method python python

Getting the keyword arguments actually passed to a Python method


I was inspired by lost-theory's decorator goodness, and after playing about with it for a bit came up with this:

def actual_kwargs():    """    Decorator that provides the wrapped function with an attribute 'actual_kwargs'    containing just those keyword arguments actually passed in to the function.    """    def decorator(function):        def inner(*args, **kwargs):            inner.actual_kwargs = kwargs            return function(*args, **kwargs)        return inner    return decoratorif __name__ == "__main__":    @actual_kwargs()    def func(msg, a=None, b=False, c='', d=0):        print msg        for arg, val in sorted(func.actual_kwargs.iteritems()):            print '  %s: %s' % (arg, val)    func("I'm only passing a", a='a')    func("Here's b and c", b=True, c='c')    func("All defaults", a=None, b=False, c='', d=0)    func("Nothin'")    try:        func("Invalid kwarg", e="bogon")    except TypeError, err:        print 'Invalid kwarg\n  %s' % err

Which prints this:

I'm only passing a  a: aHere's b and c  b: True  c: cAll defaults  a: None  b: False  c:   d: 0Nothin'Invalid kwarg  func() got an unexpected keyword argument 'e'

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.


Here is the easiest and simplest way:

def func(a=None, b=None, c=None):    args = locals().copy()    print argsfunc(2, "egg")

This give the output: {'a': 2, 'c': None, 'b': 'egg'}.The reason args should be a copy of the locals dictionary is that dictionaries are mutable, so if you created any local variables in this function args 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:

def f(**kw):  acceptable_names = set('a', 'b', 'c')  if not (set(kw) <= acceptable_names):    raise WhateverYouWantException(whatever)  ...proceed...

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:

_sentinel = object():def f(a=_sentinel, b=_sentinel, c=_sentinel):   ...proceed with checks `is _sentinel`...

by making a unique object _sentinel you remove the risk that the caller might be accidentally passing None (or other non-unique default values the caller could possibly pass). This is all object() is good for, btw: an extremely-lightweight, unique sentinel that cannot possibly be accidentally confused with any other object (when you check with the is operator).

Either solution is preferable for slightly different problems.