Home > Mobile >  Why does this Python snippet regarding dictionaries work?
Why does this Python snippet regarding dictionaries work?

Time:10-23

Say we have this

>>> x = {'a': 1, 'b': 2}
>>> y = {}
>>> for k, y[k] in x.items(): pass
...
>>> y
{'a': 1, 'b': 2}

Why does this work?

Note: I saw this first here

CodePudding user response:

a, b = (c, d) unpacks the tuple from left to right and assigns a = c and b = d in that order.

x.items() iterates over key-value pairs in x. E.g. doing list(x.items()) will give [('a', 1), ('b', 2)]

for a, b in x.items() assigns the key to a, and the value to b for each key-value pair in x.

for k, y[k] in x.items() assigns the key to k, and the value to y[k] for each key-value pair in x.

You can use k in y[k] because k has already been assigned since unpacking happens left-right

You don't need to do anything in the loop because whatever you needed is done already.

Because the loop already assigned every value in x to y[k], y is now a shallow copy of x.

As the tweet you reference says, this is indeed a "terse, unintuitive, and confusing" way to do x.copy()

CodePudding user response:

This is a remarkably odd way to use the loop binding. For each item, the tuple of (key, value) is assigned into k, y[k]. Both instances of k refer to the same variable, so the subscription y[k] assigns into the dictionary y. This relies on a destructuring assignment being solved left to right; using y[k], k doesn't work as k isn't assigned the first time through the loop.

CodePudding user response:

The iterating variable is assigned values as it iterates through the iterable. In your case, k, y[k] are assigned with (key, value) from dict.items().

In your case y[k] actually calls dict.__setitem__ here which is why your y gets updated.

Example,

class T():
    def __setitem__(self, key, value):
        print(f"Assigning {key} with value {value}")


x = {"a": 2, "b": 3, "c": 4}
val = T() 

for k, val[k] in x.items():
    pass

Assigning a with value 2
Assigning b with value 3
Assigning c with value 4

In your case dict.__setitem__ is called while iterating through x.items() and dict.__setitem__ mutates your dict y so eventually you are copying x dict into y.


As an aside: I have one more "obnoxious, unintuitive, and confusing" way to copy a dict.
x  = {'a': 2, 'b': 3, 'c': 4}
it = iter(x.items())
y  = {}
[*iter(lambda : y.__setitem__(*next(it)), "Is this confusing enough??")]
print(y)
# {'a': 2, 'b': 3, 'c': 4}
  • Related