But 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.