** in argument lists has a special meaning, as covered in section 4.7 of the tutorial. The dictionary (or dictionary-like) object passed with **kwargs is expanded into keyword arguments to the callable, much like *args is expanded into separate positional arguments.
** in argument lists has a special meaning, as covered in section 4.7 of the tutorial. The dictionary (or dictionary-like) object passed with **kwargs is expanded into keyword arguments to the callable, much like *args is expanded into separate positional arguments.
The ** turns the dictionary into keyword parameters:
>>> d1 = {'a': 1, 'b': 2}
>>> d2 = {'c': 3, 'd': 4}
>>> d3 = dict(d1, **d2)
Becomes:
>>> d3 = dict(d1, c=3, d=4)
in operator for dictionary VS. in operator for dict.keys()
python - use "in" operator for dictionary in a class - Stack Overflow
optimization - Does the 'in' clause used on python dictionaries call the keys() function every time? - Stack Overflow
What exactly is the "*" operator doing here:
Videos
Main Questions are at end of post...
I'm going over the Two Sum problem on LeetCode, and the optimal solution was to use a dictionary. This involved a "look up" to see if a dictionary key existed. Can someone explain to me how exactly python "looks up" a dictionary key to see if it exists. Particularly in comparing these two lines of code:
# where n is a key, and d is a dictionary if n in d: # where n is a key, and d.keys() is a viewing object (a list??) if n in d.keys():
I was confused because these two methods took around the same time on leetcode, and I thought the second method would have a time complexity of O(n) because (I thought) it was just a list.
I did further testing with the timeit module... my code and its results are below:
# checking time complexity of dict key look up methods # basically I'm running both methods and checking how much time each takes as the dictionary grows # I thought that the second method would take longer and longer as the dictionary grew, but that doesn't seem to be the case
Code:
from timeit import Timer
in_op = Timer("x in d", "from __main__ import x, d")
key_method = Timer("x in d.keys()", "from __main__ import x, d")
for i in range(1_000_000, 10_000_001, 1_000_000):
x = i
d = {i:0 for i in range(i)}
d[i] = 'yes'
in_op_time = in_op.timeit(1000)
d = {i:0 for i in range(i)}
d[i] = 'yes'
key_method_time = key_method.timeit(1000)
print(f"{in_op_time=} and {key_method_time=}")Results:
in_op_time=3.087499999999965e-05 and key_method_time=7.250000000000312e-05 in_op_time=3.008300000001407e-05 and key_method_time=6.941599999998882e-05 in_op_time=2.9749999999939547e-05 and key_method_time=6.862500000004434e-05 in_op_time=3.0790999999918967e-05 and key_method_time=6.945800000002222e-05 in_op_time=3.0165999999942628e-05 and key_method_time=7.025000000027148e-05 in_op_time=3.0250000000009436e-05 and key_method_time=6.829099999983157e-05 in_op_time=3.00840000000413e-05 and key_method_time=6.845799999988245e-05 in_op_time=2.958299999988867e-05 and key_method_time=6.824999999999193e-05 in_op_time=2.954200000004903e-05 and key_method_time=6.891600000002995e-05 in_op_time=2.9916999999990423e-05 and key_method_time=6.833300000064213e-05
My questions are:
-
How does the first case "know" that the key doesn't exist? I understand when you try to access a key that doesn't exist, python gives an error, so I'm assuming that's the same mechanism?
-
If the dict.keys() doesn't return a list, then what does it return?
-
How does the in operator work with dict.keys()?... Like how does it bypass the returned object and go straight to looking up the key with normal dictionary methods?
__contains__ is not used when iterating. in the operator is not the same thing as the for ... in ... statement (even though it contains the word in).
Iteration either uses object.__getitem__() (passing in integers starting at 0 until an IndexError is raised), or uses the iterator protocol via the object.__iter__ method.
You probably have a __getitem__ or __iter__ method that produces values rather than keys.
You need to override the python
__iter__
method.
def __iter__(self):
for x in self.dict.keys():
yield x
How to override Python list(iterator) behaviour?
no. foo in mydict is in fact a lot faster than foo in keys_list, since dicts are hash tables, so finding the element inside it is O(1). While foo in keys_list would be O(n) (slower as the number of keys grows bigger)
But you could always test yourself:
$ python -m timeit -s "x = range(1000)" "15 in x"
1000000 loops, best of 3: 0.311 usec per loop
$ python -m timeit -s "x = dict.fromkeys(xrange(1000))" "15 in x"
10000000 loops, best of 3: 0.0515 usec per loop
So checking directly on the dict for 1000 elements is one order of magnitude faster, not considering the time of the .keys()
Actually, builtin dicts just call their __contains__ which directly invokes the C function dict_has_key which is very fast. So, doing it the first way is better because it doesn't force you to evaluate the entire sequence of keys within your dict as calling keys() does.