[Python-ideas] Proto-PEP: Preserving the order of **kwargs in a function.

Andrew Barnert abarnert at yahoo.com
Sun Apr 6 10:18:37 CEST 2014


I like it, but a couple comments:


From: Eric Snow <ericsnowcurrently at gmail.com>
Sent: Saturday, April 5, 2014 10:43 PM


> Starting in version 3.5 Python will preserve the order of keyword
> arguments as passed to a function.  This will apply only to functions
> for which the definition uses the \*\*kwargs syntax for collecting
> otherwise unspecified keyword arguments.  Only the order of those
> keyword arguments will be preserved.


Will this be an OrderedDict, or just some mapping that (unless later modified?) iterates in the order of the keywords as passed? The latter might be nice, because it allows implementations to do things like use Raymond Hettinger's special dict, or a wrapper around a native mapping, or something else that isn't appropriate for an OrderedDict implementation but is good enough for this purpose. But I'm not sure how you'd word that in the language reference; the two relevant sentences are already pretty long and complex today (in sections 6.3.4 and 8.6):

> If any keyword argument does not correspond to a formal parameter name, a TypeError exception is raised, unless a formal parameter using the syntax **identifier is present; in this case, that formal parameter receives a dictionary containing the excess keyword arguments (using the keywords as keys and the argument values as corresponding values), or a (new) empty dictionary if there were no excess keyword arguments.


> If the form “**identifier” is present, it is initialized to a new dictionary receiving any excess keyword arguments, defaulting to a new empty dictionary. Parameters after “*” or “*identifier” are keyword-only parameters and may only be passed used keyword arguments.

Also:

> Relationship to **-unpacking syntax

> -----------------------------------
> 
> The ** unpacking syntax in function calls has no special connection with
> this proposal.  Keyword arguments provided by unpacking will be treated
> in exactly the same way as they are now: ones that match defined
> parameters are gather there and the remainder will be collected into the
> ordered kwargs (just like any other unmatched keyword argument).

I think you want to explicitly specify that they will be processed after any normal keyword arguments, and in the mapping's iteration order. (Otherwise, partial, perfect-forwarding wrappers, etc. would have obvious problems.) That isn't specified by Python 3.4; the language reference (6.3.4 again, a few paragraphs down) says:

> If the syntax **expression appears in the function call, expression must evaluate to a mapping, the contents of which are treated as additional keyword arguments.


CPython 3.4, if you just changed it to use an OrderedDict instead of a dict, would put the expression's contents _before_ the normal keywords. And there are other reasonable ways to implement things that would be perfectly valid under the current language definition that might end up with the mapping's contents in reverse order, or even arbitrary order. If you want to force those implementations to change (which you obviously do), I think the language reference should reflect that.

Finally:

> Opt-out Decorator
> -----------------
>
> This is identical to the current proposal with the exception that Python
> would also provide a decorator in functools that would cause collected
> keyword arguments to be packed into a normal dict instead of an
> OrderedDict.


I think you may need a bit more of an argument against this, because there are really two issues here.

First, there's the everyday case: every function that takes **kwargs will get a little slower. The optimized C OrderedDict will hopefully make this not significant enough to worry about; if not, the opt-out decorator may be necessary.

Second, there's Guido's case: functions that keep kwargs around for later will be potentially confusing, conceptually wrong, and possibly lead to significantly less speed- or memory-efficient code, especially if they later add a whole bunch of other stuff to the stored kwargs dict. The C OrderedDict isn't going to help here. The opt-out decorator would, but just storing dict(kwargs) instead of kwargs solves it more explicitly, just as simply, and with no more changes to existing code. So, the only reason the opt-out decorator would be necessary for this case is if the cost of that dict(kwargs) is too high. Which seems unlikely for most realistic cases—the performance issue is about adding thousands of items later to an initially-small kwargs dict, not about receiving thousands of keywords, right?

On the other hand, I understand Nick's point about not trying to answer arguments that people may never make in your PEP, so maybe it's better to leave this part as-is.


More information about the Python-ideas mailing list