On Mar 20, 2014, at 5:58, MRAB
No, a **kwargs parameter packs the keyword arguments into a dict, and a **kwargs argument unpacks a dict into the keyword arguments; that's exactly what they do. And dicts have arbitrary order.
When I decompile, what I get is:
dis(compile('''wrappee(**kwargs)''', '<string>', 'exec')) 1 0 LOAD_NAME 0 (wrappee) 3 LOAD_NAME 1 (kwargs) 6 CALL_FUNCTION_KW 0 (0 positional, 0 keyword pair) 9 POP_TOP 10 LOAD_CONST 0 (None) 13 RETURN_VALUE
This is basically an optimization, where the unpacking happens inside CALL_FUNCTION_KW instead of in a bunch of separate bytecodes that pushed them onto the stack to be popped off by CALL_FUNCTION. The end result is identical. Inside CALL_FUNCTION_KW, instead of starting with an empty dict and then popping keywords into it, it starts with a copy of kwargs and then pops keywords into it. If that weren't true, you wouldn't be able to do this: def foo(a, **kwargs): pass foo(**{'a': 1, 'b': 2}) Or: def foo(**kwargs): pass foo(b=2, **{'a': 1}) (If you replace a or b with self, this should look more familiar.) And, even if you don't have any missing or extra arguments, you are still going to get a new dict inside the callee. Otherwise this would do the wrong thing: def foo(**kwargs): kwargs['a'] = 3 d = {'a': 1, 'b': 2} foo(**d) And, even if you changed Python so that, in the case where there are no extra or missing keywords and the dict isn't mutated inside foo, you just passed it as-is and skipped the copy, that _still_ wouldn't solve the forwarding problem, because it's still just a dict in arbitrary order. And adding a separate hidden parameter to pass the order along still wouldn't help unless you also forwarded _that_ parameter somehow. And as far as I can see, there is no way to solve this problem. The only way to add keyword order information without breaking all existing forwarding functions is to make the **kwargs parameter always preserve order (and make the **kwargs calling syntax preserve order if present)--that is, to make it always an OrderedDict, which Guido has already rejected.