kw param question

kj no.email at please.post
Tue Aug 4 12:07:34 EDT 2009


In <pan.2009.08.04.04.06.14 at REMOVE.THIS.cybersource.com.au> Steven D'Aprano <steven at REMOVE.THIS.cybersource.com.au> writes:

>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


Thanks for this.  It's very useful.  A lot of stuff for me to chew
on.

kynn



More information about the Python-list mailing list