[Python-ideas] Preserving **kwargs order (was: Re: OrderedDict literals)

Andrew Barnert abarnert at yahoo.com
Tue Apr 15 05:11:53 CEST 2014


From: Franklin? Lee <leewangzhong+python at gmail.com>
Sent: Monday, April 14, 2014 7:20 PM


>Would string interning help?
>
>As in, all keywords in a **pack have to be strings. All parameter names can be interned (if they're not already). The **pack can be a special dict optimized for strings (more specifically: identifiers), with an order-preserving overhead. (And then **unpacking would be smart about the type of thing being unpacked.)

The dict unpacked into keyword arguments is not the same as the dict passed to the keyword-dict parameter. Some trivial examples:

    def spam(a):
        pass
    spam(**{'a': 1})

    def eggs(**kw):

        pass
    eggs(a=1)

    def beans(**kw):

        return kw
    d = {'a': 1}
    assert beans(**d) is not d

Also, I think you've got packing and unpacking backward. There's no way **unpack can be smart about the type of thing being unpacked, because it explicitly allows for any kind of mapping, supplied by the user, and unpacks its keys as if they were keyword arguments.

Anyway, even if things did work this way, one of Guido's objections is that he writes lots of code that keeps the kwargs dict around and adds to it later. (Presumably he has a method that takes **kw and stores it in an instance attribute, although he didn't give examples.) Using something which is not a dict, but only fakes it just well enough for passing keyword arguments, surely isn't going to satisfy him.

And other people do things to their kwargs dicts that you'd have to support. For example, there's plenty of Python 2.x code that fakes keyword-only arguments by calling kwargs.pop; that code still runs in Python 3.4, and I don't think you want to break it in 3.5. So you're effectively going to have to build a full MutableMapping.

Finally:


>I understand that there would need to be a C proof-of-concept that 

>this could act like a dict on the Python level, and maybe even beat a dict for **kwargs performance.

You really think so?

First, I don't know that hashing the strings (which can be cached!) is the slow part of this. But, if it is, hashing the strings to put them into an interned string table, and then hashing those interned pointers to put into the mini-dict, isn't going to be faster. It could be _smaller_, but I don't think anyone cares about the space used for a kwargs dict during the function calling machinery. 

Guido _does_ care about the space used for a kwargs dict that he chooses to save inside the function and later add a bunch of stuff to. But if you're worried about that case, providing something that's kind of like a dict but not is just wrong. This code has to work:

    class Spam:
        def __init__(self, **kwargs):
            self.config = kwargs
        def config(self, key, value):
            self.config[key] = value
    spam = Spam(count=10)
    spam.config(42, 'forty-two')

So you're talking about implementing the entire MutableMapping interface, at which point I don't think you have a mini-dict at all.



More information about the Python-ideas mailing list