I came across the following interesting construct:
assuming you have a list of lists as follows:
my_list = [[\'captain1\', \'foo1\', \'bar1\', \'foobar1\
As I said in comment that's because in a dictionary comprehension python evaluates the value first. And as a more pythonic approach you can use unpacking variables for this task, instead of popping from list in each iteration:
In [32]: my_list = [['captain1', 'foo1', 'bar1', 'foobar1'], ['captain2', 'foo2', 'bar2', 'foobar2']]
In [33]: {frist: {"column{}".format(i): k for i, k in enumerate(last, 1)} for frist, *last in my_list}
Out[33]:
{'captain2': {'column3': 'foobar2', 'column1': 'foo2', 'column2': 'bar2'},
'captain1': {'column3': 'foobar1', 'column1': 'foo1', 'column2': 'bar1'}}
Regarding the strange behavior of python in evaluating the keys and values in a dictionary comprehension, after some experiments I realized that this behavior is somehow reasonable rather than being a bug.
I'll brake down my impression in following parts:
In an assignment expression, python evaluates the right side first. from doc:
Python evaluates expressions from left to right. Notice that while evaluating an assignment, the right-hand side is evaluated before the left-hand side.
Dictionary comprehension is an expression and will be evaluated left to right but since there is an assignment under the hood, after translating it by python. the value which is the right had side will be evaluated first.
for example the following comprehension:
{b.pop(0): b.pop(0) for _ in range(1)}
is equivalent with following snippet:
def dict_comprehension():
the_dict = {}
for _ in range(1):
the_dict[b.pop(0)] = b.pop(0)
return the_dict
Here are some examples:
In [12]: b = [4, 0]
# simple rule : Python evaluates expressions from left to right.
In [13]: [[b.pop(0), b.pop(0)] for _ in range(1)]
Out[13]: [[4, 0]]
In [14]: b = [4, 0]
# while evaluating an assignment (aforementioned rule 1), the right-hand side is evaluated before the left-hand side.
In [15]: {b.pop(0): b.pop(0) for _ in range(1)}
Out[15]: {0: 4}
In [16]: b = [4, 0]
# This is not a dictionary comprehension and will be evaluated left to right.
In [17]: {b.pop(0): {b.pop(0) for _ in range(1)}}
Out[17]: {4: {0}}
In [18]: b = [4, 0]
# This is not a dictionary comprehension and will be evaluated left to right.
In [19]: {b.pop(0): b.pop(0) == 0}
Out[19]: {4: True}
In [20]: b = [4, 0]
# dictionary comprehension.
In [21]: {b.pop(0): {b.pop(0) for _ in range(1)} for _ in range(1)}
Out[21]: {0: {4}}
Regarding the disparity between the the fact (Or it's better to say abstraction) that dictionary comprehensions are expression and should be evaluated left to right (based on python documentation) with the observed behaviors I think it's actually a problem and immaturity of the python documentation and not a bug in python code. Because it's not reasonable at all to change the functionality because of the having a consistent documentation without any exception.