kw param question

Steven D'Aprano steven at REMOVE.THIS.cybersource.com.au
Tue Aug 4 06:06:15 CEST 2009


On Mon, 03 Aug 2009 19:59:23 +0000, kj wrote:

> I want to write a decorator that, among other things, returns a function
> that has one additional keyword parameter, say foo=None.
> 
> When I try
> 
> def my_decorator(f):
>     # blah, blah
>     def wrapper(*p, foo=None, **kw):
>         x = f(*p, **kw)
>         if (foo):
>             # blah, blah
>         else
>             # blah blah
>     return wrapper
> 
> ...i get a syntax error where it says "foo=None".  I get similar errors
> with everything else I've tried.
> 
> Is is possible to do this in Python?

Have you tried this under Python 2.6 or 3.0?

I've run into similar issues, because you can't have keyword-only 
arguments in Python 2.5 :(

My solution was to create a decorator that faked them. The docstring is 
longer than the decorator itself.


from functools import wraps

def keywords(**defaults):
    """Return a decorator which decorates a function to accept keyword
    arguments.

    Python 2.5 and earlier don't allow keyword-only arguments for 
    non-builtin functions. The obvious syntax:

    def func(x, *args, key=None, word='parrot'):

    is not permitted. As a work-around, write your function something
    like the following example:

    >>> @keywords(key=None, word='parrot')
    ... def func(x, y=0, *args, **kwargs):
    ...     # Inside the function, we can guarantee that kwargs['key'] and
    ...     # kwargs['word'] both exist, and no other keys.
    ...     print "x=%s, y=%s, args=%s" % (x, y, args)
    ...     if kwargs['key'] is None: msg = "kwargs['key'] is None"
    ...     else: msg = "kwargs['key'] is something else"
    ...     msg += " and kwargs['word'] is %r" % kwargs['word']
    ...     print msg
    ...

    When you call func, if you don't provide a keyword-only argument, the
    default will be substituted:

    >>> func(1, 2, 3, 4)
    x=1, y=2, args=(3, 4)
    kwargs['key'] is None and kwargs['word'] is 'parrot'
    >>> func(1)
    x=1, y=0, args=()
    kwargs['key'] is None and kwargs['word'] is 'parrot'


    Naturally you can provide your own values for keyword-only arguments:

    >>> func(1, 2, 3, word='spam')
    x=1, y=2, args=(3,)
    kwargs['key'] is None and kwargs['word'] is 'spam'
    >>> func(1, 2, 3, word='spam', key=len)
    x=1, y=2, args=(3,)
    kwargs['key'] is something else and kwargs['word'] is 'spam'

    If you pass an unexpected keyword argument, TypeError is raised:

    >>> #doctest:+IGNORE_EXCEPTION_DETAIL
    ... func(1, 2, 3, 4, keyword='something')
    Traceback (most recent call last):
      ...
    TypeError: ...

    """
    def decorator(func):
        """Decorate func to allow keyword-only arguments."""
        @wraps(func)
        def f(*args, **kwargs):
            for key in kwargs:
                if key not in defaults:
                    raise TypeError(
                    "'%s' is an invalid keyword argument for " \
                    "this function" % key
                    )
            d = defaults.copy()
            d.update(kwargs)
            return func(*args, **d)
        return f
    return decorator




(Copy and pasted from working code, but I make no guarantee that it will 
have survived the process in working order!)


-- 
Steven



More information about the Python-list mailing list