3 ms·
> >> out_args = [] >> out_args[3] = 'foo' Yes, this should be a typeerror. I elided any kind of error checking, but you still don't need to sort to check tha
by joshuamorton 3y ago
> >> out_args = []
>> out_args[3] = 'foo'
Yes, this should be a typeerror. I elided any kind of error checking, but you still don't need to sort to check that. You run into the same issue if you pass `*range(5)` to a function of one argument today.
> magic_kwargs = { 'arg1': 'bar', 2: 'foo', 'arg2': 'yeah', 1: 'not', 0: 'really'}
Again, this would be a TypeError (assuming the underlying function is defined as `def (arg1, arg2, ...) ` or something. And yet again, there's no need to sort anything to detect that error.
I feel like the thing you're getting at here is that you get index out of range errors given the half-assed implementation I did, which is absolutely true. It's buggy, it's broken, it's demo code. But you solve that by finding the max value of the integers keys of the dict, but finding the maximum value at worst requires a second linear scan, not a sort. You never need to sort the keys, you can always place them or error in linear time.
For a fuller implementation:
def convert_dict_to_args(f, magic_kwargs):
_max = 0
for k in magic_kwargs.keys():
if isinstance(k, int):
if k < 0: raise TypeError
if k > _max: _max = k
elif not isinstance(k, str):
raise TypeError
_SENTINEL = object()
out_args = [_SENTINEL for _ in range(_max)
out_kwargs = {}
for k, v in magic_kwargs.items():
if isinstance(k, int):
out_args[k] = v
else:
out_kwargs[k] = v
if _SENTINEL in out_args:
raise TypeError
return f(*out_args, **out_kwargs)
And this version functions as expected, still without any sorting required:
args = ('really', 'not', 'foo')
kwargs = {'arg1': 'bar', 'arg2': 'yeah'}
- necovek 3y agoYou've basically sorted those int-keyed elements into a list, only optimized by making a few assumptions (eg. that all indexes are present up to a max: if you want to error check that like a real implementation would have to, you lose some of that optimization since you now have to keep track of missing elements, which is at least another loop through the list). All of these optimizations are applicable to sorting a sparse, bounded list of integers, but that's still sorting them.
- joshuamorton 3y ago> You've basically sorted those int-keyed elements into a list No, I've copied them into a list. There was no sorting. I've never compared elements or done any nlogn operations. I've shuffled, perhaps, or ordered, but not sorted. That's a meaningful distinction. > have to keep track of missing elements, which is at least another loop through the list). The implementation I have is complete, it errors if the array is sparse. You need at most two loops through the dict, which if you wanted to you could avoid with a small (constant time and constant space) change to the underlying dict implementation to cache the max integer key stored and require only a single iteration. You can technically do even faster than that by making the iteration over the number of positional args, not total args, but in practice that would probably be slower. And it's all going to be just as fast as kwargs today. Like jumping back a bit your argument is > Passing a dict is passing by reference, and if the called method reads 5 elements from that dict, it's tiny constant time. But if you pass **kwargs to a method, the dict size is unbounded, so it's O(n), not constant! Exactly the same as now. And if the method being called declares it's params explicitly, it's still constant since you can immediately error on the first unknown parameter! If it was constant before, it's still constant. If it was linear before, it's still linear. And you never need to sort anything. Just to further clarify, above you say > and **kwargs has every element read in O(1) time or it gets passed along by reference. Reading every element in O(1) time is O(n)! And is precisely the same as what's happening now.
- necovek 3y agoIf you only need 5 kwargs, reading 5 elements from a 500k element dict is O(1). Just finding the max index is O(n). You might be right that even passing 500k element dict to a function **kwargs has terrible performance today anyway (I haven't checked but it does sound plausible from your timing tests). It seems like we are now looking for imprecisions in each other's reply and discussion does not seem to be constructive: we are agreeing on rough potential implementations, it's just that we differ whether that should be acceptable or not. That's fine, you are not convincing me and I am not convincing you, so we might as well drop it. It's not like this proposal is happening :) Have a good day!