4 ms·
Lists (like *args) are by definition ordered, and dicts (and thus **kwargs) are by definition unordered key-value pairs. This would be very confusing in sudden
by necovek 3y ago
Lists (like *args) are by definition ordered, and dicts (and thus **kwargs) are by definition unordered key-value pairs.
This would be very confusing in suddenly giving dicts a half-ordered, half-not semantics in a particular context.
Why wouldn't you simply switch to setting everything through kwargs instead?
Not to mention that this would have a performance penalty for dicts as they would have to keep an ordered set of integer keys in case they are used with **.
- ledauphin 3y agoin Python, dicts are actually definitionally ordered, since 3.7, so this objection doesn't carry much weight. additionally, you might be interested to know that the implementation of dicts as ordered was found to be more performant than its predecessor. Finally, even if they were unordered, there would be no need for ordering in this proposal, which depends on unique integer keys starting at 0.
- necovek 3y agoThe fact that they are definitionally ordered does not really matter for this discussion: for all intents and purposes of this proposal, they are unordered (insertion order is pretty much arbitrary order when it comes to their use as positional arguments or kwargs). And my intent was a bit more specific: I was thinking of "sorted", so definitely my bad. For the proposed use case, either **kwargs would have to sort a subset of the dict with integer keys to map positional arguments to their, ahem, positions, and then either leave the rest as-is (or sort it altogether), or a dict would have its definition modified to keep this order as items are added. I dislike both, and I definitely hate implicit sorting happening with **.
- ali_m 3y agoDicts already have confusing half-ordered, half-not semantics. As of 3.7 they are guaranteed to be insertion-ordered, but operators like == don't care about order.
- pphysch 3y agoI don't think it's confusing or even on the level of "semantics": looping over dict keys/elements is a common pattern and the 3.7 change makes that a little more predictable and consistent. Two dicts with same elements not being equal would be surprising and confusing.
- ali_m 3y agoIf we're treating them as ordered containers then it really ought to be surprising and confusing that two dicts with the same elements in a different order are considered equal. Other ordered containers such as lists or tuples don't behave this way. I'd use an `OrderedDict` in situations where I have a mapping where the order of elements is significant. `OrderedDict` signals intent much more clearly, and its `__eq__` method cares about order.
- necovek 3y agoYes, but I hope nobody really relies on that ordering in their code, as, as you say, it is pretty inconsistent. Eg. you can't rely on dict_b_keys = iter(dict_b) for key in dict_a: value_a = dict_a[key] value_b = dict_b[next(dict_b_keys)] print(f"{value_a} == {value_b}: ", value_a == value_b) The problem with the proposal is that it will be even stronger: you either require '*' to sort in-place (I assumed that would be even worse and thus unacceptable, so I ignored that case — it seems others believe that to be a more realistic implementation), or require dicts to maintain a half-sorted map of integer keys.
- Hamuko 3y agoDicts are ordered as of Python 3.7. >the insertion-order preservation nature of dict objects has been declared to be an official part of the Python language spec. https://docs.python.org/3/whatsnew/3.7.html https://docs.python.org/3/whatsnew/3.7.html
- SushiHippie 3y agoPython dictionaries are guaranteed to preserve insertion order since python 3.7
- JulianWasTaken 3y agoBesides the mistake about dicts being ordered in Python which was pointed out by others, > Why wouldn't you simply switch to setting everything through kwargs instead? Not all arguments in Python are passable both ways. In def f(a, /): print(a) `a` may be passed only positionally.
- kstrauser 3y agoI'd argue that functions with only position arguments are a place where you'd never want to use this proposal, ever. A tuple or list would be the right data structure to pass in there.
- JulianWasTaken 3y agoOf course it would be irrelevant for a function that only took positional arguments. I was demonstrating the existence of such things in general since OP seemed unaware. For the proposal, you'd of course only use it for functions that took more kinds of arguments.
- necovek 3y agoThanks for pointing it out. Still, it seems highly unlikely someone would decide to use such a function declaration yet want to use the proposed syntax to pass both positional and keyword arguments.
- fbdab103 3y agoJust laughing at the Cunningham's Law response of everyone jumping in to let you know that dictionaries are now insertion ordered.
- throwmeout123 3y agoThe proposal in question is not that good so maybe the order of the dicts is more interesting :P
- necovek 3y agoWhat I meant was "sorted", which is a bit more specific than "ordered", but it serves me right :) The funniest part is that this does not really matter for this discussion: insertion order is pretty much arbitrary when what you need is for key 0 to come first, key 1 to come next...
- joshuamorton 3y ago> Not to mention that this would have a performance penalty for dicts as they would have to keep an ordered set of integer keys in case they are used with *. ...why? I don't love this proposal, but `{1:"a", 2:"b", 0:"c"} == {0:"c", 1:"a", 2:"b"}`, and so presumably either would have the same behavior when passed to `def f(foo, bar baz): ...`. That the runtime would correct the order as part of the splat operation seems implied?
- necovek 3y agoPassing a huge dict and assuming the runtime would magically do the sorting was exactly an implementation I ignored because it sounded preposterous to me. Just imagine passing in a 500k item dict to ** by accident: dicts maintaining sorted state appeared the only sensible implementation to me.
- joshuamorton 3y agoBut again: why do you need to care about the whole sorted state thing? "Sorting" a dictionary keyed by index is linear time. Like the cost of doing that isn't going to be greater than the existing cost of keyword arguments, which can already be arbitrarily ordered! Like, what about this is more "preposterous" than passing a massive dict to kwargs already?
- necovek 3y agoPassing a dict is passing by reference, and if the called method reads 5 elements from that dict, it's tiny constant time. Sorting of a large collection happening implicitly is too expensive for what would generally be a benign mistake.
- joshuamorton 3y agoBut again: you don't actually need to sort anything. Like that's really important you don't need to sort anything! Passing kwargs requires resolving n references, one for each kwarg. Passing args by index requires passing n references. Like in code: def convert_dict_to_args(f, magic_kwargs): out_args = [] out_kwargs = {} for k, v in magic_kwargs: if isinstance(k, int): out_args[k] = v else: out_kwargs[k] = v return f(*out_args, **out_kwargs) There's no sorting necessary! There's potential particular optimizations that might need to happen to make this work with python's opcodes for fast function calls, since out_args isn't constructed as a tuple which is internally optimized in function calls, but afaik you're already not getting those optimizations/fast paths if you're passing by name and/or using kwargs, so this isn't a change. Edit: for context, >>> from timeit import timeit as t >>> t(stmt='f(1,2,3)', setup='def f(a, b, c): pass', number=int(1e8)) 5.765844099999981 >>> t(stmt='f(*(1,2,3))', setup='def f(a, b, c): pass', number=int(1e8)) 5.840746500000023 >>> t(stmt='f(a=1,b=2,c=3)', setup='def f(a, b, c): pass', number=int(1e8)) 7.400104499999998 >>> t(stmt='f(**d)', setup='d=dict(a=1,b=2,c=3)\ndef f(a, b, c): pass', number=int(1e8)) 20.779321399999958 >>> t(stmt='f(**d)', setup='d=dict()\ndef f(a=None, b=None, c=None): pass', number=int(1e8)) 9.583819900000094 So yeah using kwargs at all has a significant performance penalty, so you're already hitting performance penalties for using kwargs, keeping a sorted view isn't going to help sinificantly.