NameError in Python Nested for loops of List Comprehension

Question:

Scenerio:

for i in range(6):
    for j in range(i):
        j

AFAIK, in list comprehension the right most for is the outer one so, I thought the following code will work:

[ j for j in range(i) for i in range(6)]

But to my surprise, it throws a NameError

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'i' is not defined

I wonder why it didn’t work. Is it because python evaluates expression from Left to Right?
Cause, I have resolved the issue by using parenthesis:

[ (j for j in range(i)) for i in range(6)]

which outputs a bunch of generator expressions:

[<generator object <listcomp>.<genexpr> at 0x7f3b42200d00>, <generator object <listcomp>.<genexpr> at 0x7f3b42200d58>, <generator object <listcomp>.<genexpr> at 0x7f3b42200db0>, <generator object <listcomp>.<genexpr> at 0x7f3b42200e08>, <generator object <listcomp>.<genexpr> at 0x7f3b42200e60>, <generator object <listcomp>.<genexpr> at 0x7f3b42200eb8>]

To explore what is inside these generator expressions we can simply cast them into lists i.e.

[ list(j for j in range(i )) for i in range(6)]

and the output is as expected:

[[], [0], [0, 1], [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4]]

I just want to know what is really happening here.

Asked By: Wasi

||

Answers:

this code

j for j in range(i) for i in range(6) 

just like :

for j in range(i):
    for i in range(6):
        j

outer loop uses i before it is defined, so NameError occurred, i.e. your belief “the right most for is the outer one” is wrong.

You can use this code

[j for i in range(6) for j in range(i)]

and why below code work

[ (j for j in range(i)) for i in range(6)]
# parentheses make it work like this
for i in range(6):
     for j in range(i):
          j
Answered By: Kr.98

Correct, it’s evaluated from left to right. To add the others’ answers, I looked up the official explanation in the documentation.

List comprehensions have the form:

 [ expression for expr in sequence1
              for expr2 in sequence2 ...
              for exprN in sequenceN
              if condition ]

The for…in clauses contain the sequences to be iterated over. The
sequences do not have to be the same length, because they are not
iterated over in parallel, but from left to right; this is explained
more clearly in the following paragraphs. The elements of the
generated list will be the successive values of expression. The final
if clause is optional; if present, expression is only evaluated and
added to the result if condition is true.

To make the semantics very clear, a list comprehension is equivalent
to the following Python code:

for expr1 in sequence1:
    for expr2 in sequence2:
    ...
        for exprN in sequenceN:
             if (condition):
                  # Append the value of
                  # the expression to the
                  # resulting list.
Answered By: deadvoid
I am having similar issue. Please help me identify the 
problem

predictors = ['COUPON', 'HI', 'DISTANCE']
responses = [ 'PAX', 'S_INCOME', 'E_INCOME' ]

model_df = pd.DataFrame(coef_list, index = responses, columns = predictors)
fare_df = pd.Series(fare_coefs, index=predictors)
resp_con_dict = pd.Series(resp_con_list, index=responses)
pred_con_dict = pd.Series(pred_con_list, index= predictors)
model.fares = Var(predictors, domain=NonNegativeReals)

#Constraints
model.con = ConstraintList()
for r in responses:
model.con.add(
    sum(model_df.loc[r,p] * model.fares[p]
        for p in predictors) <= resp_con_dict[p])

NameError                                 Traceback 
(most recent call last)
/tmp/ipykernel_695/438487687.py in <cell line: 29>()
 30     model.con.add(
 31     sum(model_df.loc[r,p] * model.fares[p]
---> 32        for p in predictors) <= 
resp_con_dict[p])

NameError: name 'p' is not defined
Answered By: Mario Jonfiah