Why can I use a list index as an indexing variable in a for loop? [duplicate]
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}
This question already has an answer here:
Why are arbitrary target expressions allowed in for-loops?
4 answers
Are for-loop name list expressions legal?
2 answers
Why is using a list subscription allowed in a for loop? [duplicate]
1 answer
I have the following code:
a = [0,1,2,3]
for a[-1] in a:
print(a[-1])
The output is:
0
1
2
2
I'm confused about why a list index can be used as an indexing variable in a for loop.
python for-loop indexing
New contributor
marked as duplicate by cs95
StackExchange.ready(function() {
if (StackExchange.options.isMobile) return;
$('.dupe-hammer-message-hover:not(.hover-bound)').each(function() {
var $hover = $(this).addClass('hover-bound'),
$msg = $hover.siblings('.dupe-hammer-message');
$hover.hover(
function() {
$hover.showInfoMessage('', {
messageElement: $msg.clone().show(),
transient: false,
position: { my: 'bottom left', at: 'top center', offsetTop: -7 },
dismissable: false,
relativeToBody: true
});
},
function() {
StackExchange.helpers.removeMessages();
}
);
});
});
Apr 13 at 0:41
This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.
|
show 1 more comment
This question already has an answer here:
Why are arbitrary target expressions allowed in for-loops?
4 answers
Are for-loop name list expressions legal?
2 answers
Why is using a list subscription allowed in a for loop? [duplicate]
1 answer
I have the following code:
a = [0,1,2,3]
for a[-1] in a:
print(a[-1])
The output is:
0
1
2
2
I'm confused about why a list index can be used as an indexing variable in a for loop.
python for-loop indexing
New contributor
marked as duplicate by cs95
StackExchange.ready(function() {
if (StackExchange.options.isMobile) return;
$('.dupe-hammer-message-hover:not(.hover-bound)').each(function() {
var $hover = $(this).addClass('hover-bound'),
$msg = $hover.siblings('.dupe-hammer-message');
$hover.hover(
function() {
$hover.showInfoMessage('', {
messageElement: $msg.clone().show(),
transient: false,
position: { my: 'bottom left', at: 'top center', offsetTop: -7 },
dismissable: false,
relativeToBody: true
});
},
function() {
StackExchange.helpers.removeMessages();
}
);
});
});
Apr 13 at 0:41
This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.
28
Somehow this question looks like a bad and newbie question, but I don't get the logic either lol
– gameon67
Apr 12 at 3:55
13
I don't know why you would ever want to do this, but now I know you can
– Nathan
Apr 12 at 3:57
over the iteration only the last time a[-1] gets its value, all other times python treats as iteration variable.
– Arun Augustine
Apr 12 at 4:01
41
This would be a great question for an awful coding interview
– Nathan
Apr 12 at 4:04
2
Btw OP, nice first question!
– Christian Dean
Apr 12 at 23:21
|
show 1 more comment
This question already has an answer here:
Why are arbitrary target expressions allowed in for-loops?
4 answers
Are for-loop name list expressions legal?
2 answers
Why is using a list subscription allowed in a for loop? [duplicate]
1 answer
I have the following code:
a = [0,1,2,3]
for a[-1] in a:
print(a[-1])
The output is:
0
1
2
2
I'm confused about why a list index can be used as an indexing variable in a for loop.
python for-loop indexing
New contributor
This question already has an answer here:
Why are arbitrary target expressions allowed in for-loops?
4 answers
Are for-loop name list expressions legal?
2 answers
Why is using a list subscription allowed in a for loop? [duplicate]
1 answer
I have the following code:
a = [0,1,2,3]
for a[-1] in a:
print(a[-1])
The output is:
0
1
2
2
I'm confused about why a list index can be used as an indexing variable in a for loop.
This question already has an answer here:
Why are arbitrary target expressions allowed in for-loops?
4 answers
Are for-loop name list expressions legal?
2 answers
Why is using a list subscription allowed in a for loop? [duplicate]
1 answer
python for-loop indexing
python for-loop indexing
New contributor
New contributor
edited Apr 12 at 11:01
Peter Mortensen
13.9k1987114
13.9k1987114
New contributor
asked Apr 12 at 3:45
Kundan VermaKundan Verma
21734
21734
New contributor
New contributor
marked as duplicate by cs95
StackExchange.ready(function() {
if (StackExchange.options.isMobile) return;
$('.dupe-hammer-message-hover:not(.hover-bound)').each(function() {
var $hover = $(this).addClass('hover-bound'),
$msg = $hover.siblings('.dupe-hammer-message');
$hover.hover(
function() {
$hover.showInfoMessage('', {
messageElement: $msg.clone().show(),
transient: false,
position: { my: 'bottom left', at: 'top center', offsetTop: -7 },
dismissable: false,
relativeToBody: true
});
},
function() {
StackExchange.helpers.removeMessages();
}
);
});
});
Apr 13 at 0:41
This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.
marked as duplicate by cs95
StackExchange.ready(function() {
if (StackExchange.options.isMobile) return;
$('.dupe-hammer-message-hover:not(.hover-bound)').each(function() {
var $hover = $(this).addClass('hover-bound'),
$msg = $hover.siblings('.dupe-hammer-message');
$hover.hover(
function() {
$hover.showInfoMessage('', {
messageElement: $msg.clone().show(),
transient: false,
position: { my: 'bottom left', at: 'top center', offsetTop: -7 },
dismissable: false,
relativeToBody: true
});
},
function() {
StackExchange.helpers.removeMessages();
}
);
});
});
Apr 13 at 0:41
This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.
28
Somehow this question looks like a bad and newbie question, but I don't get the logic either lol
– gameon67
Apr 12 at 3:55
13
I don't know why you would ever want to do this, but now I know you can
– Nathan
Apr 12 at 3:57
over the iteration only the last time a[-1] gets its value, all other times python treats as iteration variable.
– Arun Augustine
Apr 12 at 4:01
41
This would be a great question for an awful coding interview
– Nathan
Apr 12 at 4:04
2
Btw OP, nice first question!
– Christian Dean
Apr 12 at 23:21
|
show 1 more comment
28
Somehow this question looks like a bad and newbie question, but I don't get the logic either lol
– gameon67
Apr 12 at 3:55
13
I don't know why you would ever want to do this, but now I know you can
– Nathan
Apr 12 at 3:57
over the iteration only the last time a[-1] gets its value, all other times python treats as iteration variable.
– Arun Augustine
Apr 12 at 4:01
41
This would be a great question for an awful coding interview
– Nathan
Apr 12 at 4:04
2
Btw OP, nice first question!
– Christian Dean
Apr 12 at 23:21
28
28
Somehow this question looks like a bad and newbie question, but I don't get the logic either lol
– gameon67
Apr 12 at 3:55
Somehow this question looks like a bad and newbie question, but I don't get the logic either lol
– gameon67
Apr 12 at 3:55
13
13
I don't know why you would ever want to do this, but now I know you can
– Nathan
Apr 12 at 3:57
I don't know why you would ever want to do this, but now I know you can
– Nathan
Apr 12 at 3:57
over the iteration only the last time a[-1] gets its value, all other times python treats as iteration variable.
– Arun Augustine
Apr 12 at 4:01
over the iteration only the last time a[-1] gets its value, all other times python treats as iteration variable.
– Arun Augustine
Apr 12 at 4:01
41
41
This would be a great question for an awful coding interview
– Nathan
Apr 12 at 4:04
This would be a great question for an awful coding interview
– Nathan
Apr 12 at 4:04
2
2
Btw OP, nice first question!
– Christian Dean
Apr 12 at 23:21
Btw OP, nice first question!
– Christian Dean
Apr 12 at 23:21
|
show 1 more comment
6 Answers
6
active
oldest
votes
List indexes such as a[-1]
in the expression for a[-1] in a
are valid as specified by the for_stmt
(and specifically the target_list
) grammar token, where slicing
is a valid target for assignment.
"Huh? Assignment? What has that got to do with my output?"
Indeed, it has everything to do with the output and result. Let's dive into the documentation for a for-in
loop:
for_stmt ::= "for" target_list "in" expression_list ":" suite
The expression list is evaluated once; it should yield an iterable object. An iterator is created for the result of the
expression_list
. The suite is then executed once for each item provided by the iterator, in the order returned by the iterator. Each item in turn is assigned to the target list using the standard rules for assignments (see Assignment statements), and then the suite is executed.
(emphasis added)
N.B. the suite refers to the statement(s) under the for-block, print(a[-1])
in our particular case.
Let's have a little fun and extend the print statement:
a = [0, 1, 2, 3]
for a[-1] in a:
print(a, a[-1])
This gives the following output:
[0, 1, 2, 0] 0 # a[-1] assigned 0
[0, 1, 2, 1] 1 # a[-1] assigned 1
[0, 1, 2, 2] 2 # a[-1] assigned 2
[0, 1, 2, 2] 2 # a[-1] assigned 2 (itself)
(comments added)
Here, a[-1]
changes on each iteration and we see this change propagated to a
. Again, this is possible due to slicing
being a valid target.
A good argument made by Ev. Kounis regards the first sentence of the quoted doc above: "The expression list is evaluated once". Does this not imply that the expression list is static and immutable, constant at [0, 1, 2, 3]
? Shouldn't a[-1]
thus be assigned 3
at the final iteration?
Well, Konrad Rudolph asserts that:
No, [the expression list is] evaluated once to create an iterable object. But that iterable object still iterates over the original data, not a copy of it.
(emphasis added)
The following code demonstrates how an iterable it
lazily yields elements of a list x
.
x = [1, 2, 3, 4]
it = iter(x)
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
x[-1] = 0
print(next(it)) # 0
(code inspired by Kounis')
If evaluation was eager, we could expect x[-1] = 0
to have zero effect on it
and expect 4
to be printed. This is clearly not the case and goes to show that by the same principle, our for
-loop lazily yields numbers from a
following assignments to a[-1]
on each iteration.
11
Essentially, the important thing to note here is that Python considersa[-1]
to be a valid form of the left-hand side of an assignment statement (e.g.a[-1] = 1
is valid grammar). Thusa[-1]
is a valid "variable" name, because as the documentation stated, it evaluates the binding variable(s) in a for loop declaration as it would the left-hand side of an assignment.
– Christian Dean
Apr 12 at 4:21
4
This answer is missing an important point, namely how the grammar definestarget_list
. For reasons that are utterly obscure to me, the grammar explicitly (rather than “accidentally”) allows the target list to contain slicing expressions (the syntax for thefor
loop simply recycles normal assignment targets, which seems odd).
– Konrad Rudolph
Apr 12 at 9:58
1
doesn't the fact thata[-1]
on the last iteration is2
contradict the documentation in that "the expression list is evaluated once"?
– Ev. Kounis
Apr 12 at 11:04
3
@Ev.Kounis No, it’s evaluated once to create an iterable object. But that iterable object still iterates over the original data, not a copy of it.
– Konrad Rudolph
Apr 12 at 11:14
2
@KonradRudolph I see..
– Ev. Kounis
Apr 12 at 11:23
|
show 3 more comments
(This is more of a long comment than an answer - there are a couple of good ones already, especially @TrebledJ's. But I had to think of it explicitly in terms of overwriting variables that already have values before it clicked for me.)
If you had
x = 0
l = [1, 2, 3]
for x in l:
print(x)
you wouldn't be surprised that x
is overridden each time through the loop. Even though x
existed before, its value isn't used (i.e. for 0 in l:
, which would throw an error). Rather, we assign the values from l
to x
.
When we do
a = [0, 1, 2, 3]
for a[-1] in a:
print(a[-1])
even though a[-1]
already exists and has a value, we don't put that value in but rather assign to a[-1]
each time through the loop.
Somehow I thought the variable used infor loop
is immutable. +1 for pointing it out.
– Amir A. Shabani
Apr 12 at 4:17
add a comment |
The left expression of a for
loop statement gets assigned with each item in the iterable on the right in each iteration, so
for n in a:
print(n)
is just a fancy way of doing:
for i in range(len(a)):
n = a[i]
print(n)
Likewise,
for a[-1] in a:
print(a[-1])
is just a fancy way of doing:
for i in range(len(a)):
a[-1] = a[i]
print(a[-1])
where in each iteration, the last item of a
gets assigned with the next item in a
, so when the iteration finally comes to the last item, its value got last assigned with the second-last item, 2
.
add a comment |
It is an interesting question, and you can understand it by that:
for v in a:
a[-1] = v
print(a[-1])
print(a)
actually a
becomes: [0, 1, 2, 2]
after loop
Output:
0
1
2
2
[0, 1, 2, 2]
I hope that helps you, and comment if you have further questions. : )
add a comment |
The answer by TrebledJ explains the technical reason of why this is possible.
Why would you want to do this though?
Suppose I have an algorithm that operates on an array:
x = np.arange(5)
And I want to test the result of the algorithm using different values of the first index. I can simply skip the first value, reconstructing an array every time:
for i in range(5):
print(np.r_[i, x[1:]].sum())
(np.r_
)
This will create a new array on every iteration, which is not ideal, in particular if the array is large. To reuse the same memory on every iteration, I can rewrite it as:
for i in range(5):
x[0] = i
print(x.sum())
Which is probably clearer than the first version too.
But that is exactly identical to the more compact way to write this:
for x[0] in range(5):
print(x.sum())
all of the above will result in:
10
11
12
13
14
Now this is a trivial "algorithm", but there will be more complicated purposes where one might want to test changing a single (or multiple, but that complicating things due to assignment unpacking) value in an array to a number of values, preferably without copying the entire array. In this case, you may want to use an indexed value in a loop, but be prepared to confuse anyone maintaining your code (including yourself). For this reason, the second version explicitly assigning x[0] = i
is probably preferable, but if you prefer the more compact for x[0] in range(5)
style, this should be a valid use case.
What isnp.r
in the second code block?
– Nathan
Apr 13 at 2:41
@Nathan docs.scipy.org/doc/numpy/reference/generated/numpy.r_.html
– gerrit
Apr 14 at 8:35
Ah, this should benp.r_
; that's why I couldn't find it in mynumpy
.
– Nathan
Apr 14 at 18:53
@Nathan Oops! Corrected.
– gerrit
Apr 14 at 21:09
add a comment |
a[-1]
refers to the last element of a
, in this case a[3]
. The for
loop is a bit unusual in that it is using this element as the loop variable.
It's not evaluating that element upon loop entry, but rather it is assigning to it on each iteration through the loop.
So first a[-1]
gets set to 0, then 1, then 2. Finally, on the last iteration, the for
loop retrieves a[3]
which at that point is 2
, so the list ends up as [0, 1, 2, 2]
.
A more typical for
loop uses a simple local variable name as the loop variable, e.g. for x ...
. In that case, x
is set to the next value upon each iteration. This case is no different, except that a[-1]
is set to the next value upon each iteration. You don't see this very often, but it's consistent.
How does it actuallygets set to 0, then 1, then 2 and finally 2
? That's the confusing part!
– Amir A. Shabani
Apr 12 at 3:53
1
Agree, I don't really understand by reading this answer
– gameon67
Apr 12 at 3:53
I expected it would be like sayingfor 3 in a: print(a[-1])
(becausea[-1]
was3
at the start of the loop) and give an error, but clearly that's not the case. You're obviously correct about what's happening, but I'm surprised this evaluates this way.
– Nathan
Apr 12 at 3:56
I've expanded the answer to explain more precisely how this works. Hopefully people will understand it this time.
– Tom Karzes
Apr 12 at 4:05
1
"The for loop is a bit unusual in that it is using this element as the loop variable." - This may be where the confusion is coming from. Python isn't using the elementa[-1]
evaluates to, rather, it's using the indexa[-1]
itself. If you rephrase that statement, your answer becomes much more clear.
– Christian Dean
Apr 12 at 4:29
|
show 2 more comments
6 Answers
6
active
oldest
votes
6 Answers
6
active
oldest
votes
active
oldest
votes
active
oldest
votes
List indexes such as a[-1]
in the expression for a[-1] in a
are valid as specified by the for_stmt
(and specifically the target_list
) grammar token, where slicing
is a valid target for assignment.
"Huh? Assignment? What has that got to do with my output?"
Indeed, it has everything to do with the output and result. Let's dive into the documentation for a for-in
loop:
for_stmt ::= "for" target_list "in" expression_list ":" suite
The expression list is evaluated once; it should yield an iterable object. An iterator is created for the result of the
expression_list
. The suite is then executed once for each item provided by the iterator, in the order returned by the iterator. Each item in turn is assigned to the target list using the standard rules for assignments (see Assignment statements), and then the suite is executed.
(emphasis added)
N.B. the suite refers to the statement(s) under the for-block, print(a[-1])
in our particular case.
Let's have a little fun and extend the print statement:
a = [0, 1, 2, 3]
for a[-1] in a:
print(a, a[-1])
This gives the following output:
[0, 1, 2, 0] 0 # a[-1] assigned 0
[0, 1, 2, 1] 1 # a[-1] assigned 1
[0, 1, 2, 2] 2 # a[-1] assigned 2
[0, 1, 2, 2] 2 # a[-1] assigned 2 (itself)
(comments added)
Here, a[-1]
changes on each iteration and we see this change propagated to a
. Again, this is possible due to slicing
being a valid target.
A good argument made by Ev. Kounis regards the first sentence of the quoted doc above: "The expression list is evaluated once". Does this not imply that the expression list is static and immutable, constant at [0, 1, 2, 3]
? Shouldn't a[-1]
thus be assigned 3
at the final iteration?
Well, Konrad Rudolph asserts that:
No, [the expression list is] evaluated once to create an iterable object. But that iterable object still iterates over the original data, not a copy of it.
(emphasis added)
The following code demonstrates how an iterable it
lazily yields elements of a list x
.
x = [1, 2, 3, 4]
it = iter(x)
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
x[-1] = 0
print(next(it)) # 0
(code inspired by Kounis')
If evaluation was eager, we could expect x[-1] = 0
to have zero effect on it
and expect 4
to be printed. This is clearly not the case and goes to show that by the same principle, our for
-loop lazily yields numbers from a
following assignments to a[-1]
on each iteration.
11
Essentially, the important thing to note here is that Python considersa[-1]
to be a valid form of the left-hand side of an assignment statement (e.g.a[-1] = 1
is valid grammar). Thusa[-1]
is a valid "variable" name, because as the documentation stated, it evaluates the binding variable(s) in a for loop declaration as it would the left-hand side of an assignment.
– Christian Dean
Apr 12 at 4:21
4
This answer is missing an important point, namely how the grammar definestarget_list
. For reasons that are utterly obscure to me, the grammar explicitly (rather than “accidentally”) allows the target list to contain slicing expressions (the syntax for thefor
loop simply recycles normal assignment targets, which seems odd).
– Konrad Rudolph
Apr 12 at 9:58
1
doesn't the fact thata[-1]
on the last iteration is2
contradict the documentation in that "the expression list is evaluated once"?
– Ev. Kounis
Apr 12 at 11:04
3
@Ev.Kounis No, it’s evaluated once to create an iterable object. But that iterable object still iterates over the original data, not a copy of it.
– Konrad Rudolph
Apr 12 at 11:14
2
@KonradRudolph I see..
– Ev. Kounis
Apr 12 at 11:23
|
show 3 more comments
List indexes such as a[-1]
in the expression for a[-1] in a
are valid as specified by the for_stmt
(and specifically the target_list
) grammar token, where slicing
is a valid target for assignment.
"Huh? Assignment? What has that got to do with my output?"
Indeed, it has everything to do with the output and result. Let's dive into the documentation for a for-in
loop:
for_stmt ::= "for" target_list "in" expression_list ":" suite
The expression list is evaluated once; it should yield an iterable object. An iterator is created for the result of the
expression_list
. The suite is then executed once for each item provided by the iterator, in the order returned by the iterator. Each item in turn is assigned to the target list using the standard rules for assignments (see Assignment statements), and then the suite is executed.
(emphasis added)
N.B. the suite refers to the statement(s) under the for-block, print(a[-1])
in our particular case.
Let's have a little fun and extend the print statement:
a = [0, 1, 2, 3]
for a[-1] in a:
print(a, a[-1])
This gives the following output:
[0, 1, 2, 0] 0 # a[-1] assigned 0
[0, 1, 2, 1] 1 # a[-1] assigned 1
[0, 1, 2, 2] 2 # a[-1] assigned 2
[0, 1, 2, 2] 2 # a[-1] assigned 2 (itself)
(comments added)
Here, a[-1]
changes on each iteration and we see this change propagated to a
. Again, this is possible due to slicing
being a valid target.
A good argument made by Ev. Kounis regards the first sentence of the quoted doc above: "The expression list is evaluated once". Does this not imply that the expression list is static and immutable, constant at [0, 1, 2, 3]
? Shouldn't a[-1]
thus be assigned 3
at the final iteration?
Well, Konrad Rudolph asserts that:
No, [the expression list is] evaluated once to create an iterable object. But that iterable object still iterates over the original data, not a copy of it.
(emphasis added)
The following code demonstrates how an iterable it
lazily yields elements of a list x
.
x = [1, 2, 3, 4]
it = iter(x)
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
x[-1] = 0
print(next(it)) # 0
(code inspired by Kounis')
If evaluation was eager, we could expect x[-1] = 0
to have zero effect on it
and expect 4
to be printed. This is clearly not the case and goes to show that by the same principle, our for
-loop lazily yields numbers from a
following assignments to a[-1]
on each iteration.
11
Essentially, the important thing to note here is that Python considersa[-1]
to be a valid form of the left-hand side of an assignment statement (e.g.a[-1] = 1
is valid grammar). Thusa[-1]
is a valid "variable" name, because as the documentation stated, it evaluates the binding variable(s) in a for loop declaration as it would the left-hand side of an assignment.
– Christian Dean
Apr 12 at 4:21
4
This answer is missing an important point, namely how the grammar definestarget_list
. For reasons that are utterly obscure to me, the grammar explicitly (rather than “accidentally”) allows the target list to contain slicing expressions (the syntax for thefor
loop simply recycles normal assignment targets, which seems odd).
– Konrad Rudolph
Apr 12 at 9:58
1
doesn't the fact thata[-1]
on the last iteration is2
contradict the documentation in that "the expression list is evaluated once"?
– Ev. Kounis
Apr 12 at 11:04
3
@Ev.Kounis No, it’s evaluated once to create an iterable object. But that iterable object still iterates over the original data, not a copy of it.
– Konrad Rudolph
Apr 12 at 11:14
2
@KonradRudolph I see..
– Ev. Kounis
Apr 12 at 11:23
|
show 3 more comments
List indexes such as a[-1]
in the expression for a[-1] in a
are valid as specified by the for_stmt
(and specifically the target_list
) grammar token, where slicing
is a valid target for assignment.
"Huh? Assignment? What has that got to do with my output?"
Indeed, it has everything to do with the output and result. Let's dive into the documentation for a for-in
loop:
for_stmt ::= "for" target_list "in" expression_list ":" suite
The expression list is evaluated once; it should yield an iterable object. An iterator is created for the result of the
expression_list
. The suite is then executed once for each item provided by the iterator, in the order returned by the iterator. Each item in turn is assigned to the target list using the standard rules for assignments (see Assignment statements), and then the suite is executed.
(emphasis added)
N.B. the suite refers to the statement(s) under the for-block, print(a[-1])
in our particular case.
Let's have a little fun and extend the print statement:
a = [0, 1, 2, 3]
for a[-1] in a:
print(a, a[-1])
This gives the following output:
[0, 1, 2, 0] 0 # a[-1] assigned 0
[0, 1, 2, 1] 1 # a[-1] assigned 1
[0, 1, 2, 2] 2 # a[-1] assigned 2
[0, 1, 2, 2] 2 # a[-1] assigned 2 (itself)
(comments added)
Here, a[-1]
changes on each iteration and we see this change propagated to a
. Again, this is possible due to slicing
being a valid target.
A good argument made by Ev. Kounis regards the first sentence of the quoted doc above: "The expression list is evaluated once". Does this not imply that the expression list is static and immutable, constant at [0, 1, 2, 3]
? Shouldn't a[-1]
thus be assigned 3
at the final iteration?
Well, Konrad Rudolph asserts that:
No, [the expression list is] evaluated once to create an iterable object. But that iterable object still iterates over the original data, not a copy of it.
(emphasis added)
The following code demonstrates how an iterable it
lazily yields elements of a list x
.
x = [1, 2, 3, 4]
it = iter(x)
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
x[-1] = 0
print(next(it)) # 0
(code inspired by Kounis')
If evaluation was eager, we could expect x[-1] = 0
to have zero effect on it
and expect 4
to be printed. This is clearly not the case and goes to show that by the same principle, our for
-loop lazily yields numbers from a
following assignments to a[-1]
on each iteration.
List indexes such as a[-1]
in the expression for a[-1] in a
are valid as specified by the for_stmt
(and specifically the target_list
) grammar token, where slicing
is a valid target for assignment.
"Huh? Assignment? What has that got to do with my output?"
Indeed, it has everything to do with the output and result. Let's dive into the documentation for a for-in
loop:
for_stmt ::= "for" target_list "in" expression_list ":" suite
The expression list is evaluated once; it should yield an iterable object. An iterator is created for the result of the
expression_list
. The suite is then executed once for each item provided by the iterator, in the order returned by the iterator. Each item in turn is assigned to the target list using the standard rules for assignments (see Assignment statements), and then the suite is executed.
(emphasis added)
N.B. the suite refers to the statement(s) under the for-block, print(a[-1])
in our particular case.
Let's have a little fun and extend the print statement:
a = [0, 1, 2, 3]
for a[-1] in a:
print(a, a[-1])
This gives the following output:
[0, 1, 2, 0] 0 # a[-1] assigned 0
[0, 1, 2, 1] 1 # a[-1] assigned 1
[0, 1, 2, 2] 2 # a[-1] assigned 2
[0, 1, 2, 2] 2 # a[-1] assigned 2 (itself)
(comments added)
Here, a[-1]
changes on each iteration and we see this change propagated to a
. Again, this is possible due to slicing
being a valid target.
A good argument made by Ev. Kounis regards the first sentence of the quoted doc above: "The expression list is evaluated once". Does this not imply that the expression list is static and immutable, constant at [0, 1, 2, 3]
? Shouldn't a[-1]
thus be assigned 3
at the final iteration?
Well, Konrad Rudolph asserts that:
No, [the expression list is] evaluated once to create an iterable object. But that iterable object still iterates over the original data, not a copy of it.
(emphasis added)
The following code demonstrates how an iterable it
lazily yields elements of a list x
.
x = [1, 2, 3, 4]
it = iter(x)
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
x[-1] = 0
print(next(it)) # 0
(code inspired by Kounis')
If evaluation was eager, we could expect x[-1] = 0
to have zero effect on it
and expect 4
to be printed. This is clearly not the case and goes to show that by the same principle, our for
-loop lazily yields numbers from a
following assignments to a[-1]
on each iteration.
edited Apr 12 at 16:05
answered Apr 12 at 4:05
TrebledJTrebledJ
3,93921431
3,93921431
11
Essentially, the important thing to note here is that Python considersa[-1]
to be a valid form of the left-hand side of an assignment statement (e.g.a[-1] = 1
is valid grammar). Thusa[-1]
is a valid "variable" name, because as the documentation stated, it evaluates the binding variable(s) in a for loop declaration as it would the left-hand side of an assignment.
– Christian Dean
Apr 12 at 4:21
4
This answer is missing an important point, namely how the grammar definestarget_list
. For reasons that are utterly obscure to me, the grammar explicitly (rather than “accidentally”) allows the target list to contain slicing expressions (the syntax for thefor
loop simply recycles normal assignment targets, which seems odd).
– Konrad Rudolph
Apr 12 at 9:58
1
doesn't the fact thata[-1]
on the last iteration is2
contradict the documentation in that "the expression list is evaluated once"?
– Ev. Kounis
Apr 12 at 11:04
3
@Ev.Kounis No, it’s evaluated once to create an iterable object. But that iterable object still iterates over the original data, not a copy of it.
– Konrad Rudolph
Apr 12 at 11:14
2
@KonradRudolph I see..
– Ev. Kounis
Apr 12 at 11:23
|
show 3 more comments
11
Essentially, the important thing to note here is that Python considersa[-1]
to be a valid form of the left-hand side of an assignment statement (e.g.a[-1] = 1
is valid grammar). Thusa[-1]
is a valid "variable" name, because as the documentation stated, it evaluates the binding variable(s) in a for loop declaration as it would the left-hand side of an assignment.
– Christian Dean
Apr 12 at 4:21
4
This answer is missing an important point, namely how the grammar definestarget_list
. For reasons that are utterly obscure to me, the grammar explicitly (rather than “accidentally”) allows the target list to contain slicing expressions (the syntax for thefor
loop simply recycles normal assignment targets, which seems odd).
– Konrad Rudolph
Apr 12 at 9:58
1
doesn't the fact thata[-1]
on the last iteration is2
contradict the documentation in that "the expression list is evaluated once"?
– Ev. Kounis
Apr 12 at 11:04
3
@Ev.Kounis No, it’s evaluated once to create an iterable object. But that iterable object still iterates over the original data, not a copy of it.
– Konrad Rudolph
Apr 12 at 11:14
2
@KonradRudolph I see..
– Ev. Kounis
Apr 12 at 11:23
11
11
Essentially, the important thing to note here is that Python considers
a[-1]
to be a valid form of the left-hand side of an assignment statement (e.g. a[-1] = 1
is valid grammar). Thus a[-1]
is a valid "variable" name, because as the documentation stated, it evaluates the binding variable(s) in a for loop declaration as it would the left-hand side of an assignment.– Christian Dean
Apr 12 at 4:21
Essentially, the important thing to note here is that Python considers
a[-1]
to be a valid form of the left-hand side of an assignment statement (e.g. a[-1] = 1
is valid grammar). Thus a[-1]
is a valid "variable" name, because as the documentation stated, it evaluates the binding variable(s) in a for loop declaration as it would the left-hand side of an assignment.– Christian Dean
Apr 12 at 4:21
4
4
This answer is missing an important point, namely how the grammar defines
target_list
. For reasons that are utterly obscure to me, the grammar explicitly (rather than “accidentally”) allows the target list to contain slicing expressions (the syntax for the for
loop simply recycles normal assignment targets, which seems odd).– Konrad Rudolph
Apr 12 at 9:58
This answer is missing an important point, namely how the grammar defines
target_list
. For reasons that are utterly obscure to me, the grammar explicitly (rather than “accidentally”) allows the target list to contain slicing expressions (the syntax for the for
loop simply recycles normal assignment targets, which seems odd).– Konrad Rudolph
Apr 12 at 9:58
1
1
doesn't the fact that
a[-1]
on the last iteration is 2
contradict the documentation in that "the expression list is evaluated once"?– Ev. Kounis
Apr 12 at 11:04
doesn't the fact that
a[-1]
on the last iteration is 2
contradict the documentation in that "the expression list is evaluated once"?– Ev. Kounis
Apr 12 at 11:04
3
3
@Ev.Kounis No, it’s evaluated once to create an iterable object. But that iterable object still iterates over the original data, not a copy of it.
– Konrad Rudolph
Apr 12 at 11:14
@Ev.Kounis No, it’s evaluated once to create an iterable object. But that iterable object still iterates over the original data, not a copy of it.
– Konrad Rudolph
Apr 12 at 11:14
2
2
@KonradRudolph I see..
– Ev. Kounis
Apr 12 at 11:23
@KonradRudolph I see..
– Ev. Kounis
Apr 12 at 11:23
|
show 3 more comments
(This is more of a long comment than an answer - there are a couple of good ones already, especially @TrebledJ's. But I had to think of it explicitly in terms of overwriting variables that already have values before it clicked for me.)
If you had
x = 0
l = [1, 2, 3]
for x in l:
print(x)
you wouldn't be surprised that x
is overridden each time through the loop. Even though x
existed before, its value isn't used (i.e. for 0 in l:
, which would throw an error). Rather, we assign the values from l
to x
.
When we do
a = [0, 1, 2, 3]
for a[-1] in a:
print(a[-1])
even though a[-1]
already exists and has a value, we don't put that value in but rather assign to a[-1]
each time through the loop.
Somehow I thought the variable used infor loop
is immutable. +1 for pointing it out.
– Amir A. Shabani
Apr 12 at 4:17
add a comment |
(This is more of a long comment than an answer - there are a couple of good ones already, especially @TrebledJ's. But I had to think of it explicitly in terms of overwriting variables that already have values before it clicked for me.)
If you had
x = 0
l = [1, 2, 3]
for x in l:
print(x)
you wouldn't be surprised that x
is overridden each time through the loop. Even though x
existed before, its value isn't used (i.e. for 0 in l:
, which would throw an error). Rather, we assign the values from l
to x
.
When we do
a = [0, 1, 2, 3]
for a[-1] in a:
print(a[-1])
even though a[-1]
already exists and has a value, we don't put that value in but rather assign to a[-1]
each time through the loop.
Somehow I thought the variable used infor loop
is immutable. +1 for pointing it out.
– Amir A. Shabani
Apr 12 at 4:17
add a comment |
(This is more of a long comment than an answer - there are a couple of good ones already, especially @TrebledJ's. But I had to think of it explicitly in terms of overwriting variables that already have values before it clicked for me.)
If you had
x = 0
l = [1, 2, 3]
for x in l:
print(x)
you wouldn't be surprised that x
is overridden each time through the loop. Even though x
existed before, its value isn't used (i.e. for 0 in l:
, which would throw an error). Rather, we assign the values from l
to x
.
When we do
a = [0, 1, 2, 3]
for a[-1] in a:
print(a[-1])
even though a[-1]
already exists and has a value, we don't put that value in but rather assign to a[-1]
each time through the loop.
(This is more of a long comment than an answer - there are a couple of good ones already, especially @TrebledJ's. But I had to think of it explicitly in terms of overwriting variables that already have values before it clicked for me.)
If you had
x = 0
l = [1, 2, 3]
for x in l:
print(x)
you wouldn't be surprised that x
is overridden each time through the loop. Even though x
existed before, its value isn't used (i.e. for 0 in l:
, which would throw an error). Rather, we assign the values from l
to x
.
When we do
a = [0, 1, 2, 3]
for a[-1] in a:
print(a[-1])
even though a[-1]
already exists and has a value, we don't put that value in but rather assign to a[-1]
each time through the loop.
edited Apr 12 at 11:07
Peter Mortensen
13.9k1987114
13.9k1987114
answered Apr 12 at 4:14
NathanNathan
2,27511329
2,27511329
Somehow I thought the variable used infor loop
is immutable. +1 for pointing it out.
– Amir A. Shabani
Apr 12 at 4:17
add a comment |
Somehow I thought the variable used infor loop
is immutable. +1 for pointing it out.
– Amir A. Shabani
Apr 12 at 4:17
Somehow I thought the variable used in
for loop
is immutable. +1 for pointing it out.– Amir A. Shabani
Apr 12 at 4:17
Somehow I thought the variable used in
for loop
is immutable. +1 for pointing it out.– Amir A. Shabani
Apr 12 at 4:17
add a comment |
The left expression of a for
loop statement gets assigned with each item in the iterable on the right in each iteration, so
for n in a:
print(n)
is just a fancy way of doing:
for i in range(len(a)):
n = a[i]
print(n)
Likewise,
for a[-1] in a:
print(a[-1])
is just a fancy way of doing:
for i in range(len(a)):
a[-1] = a[i]
print(a[-1])
where in each iteration, the last item of a
gets assigned with the next item in a
, so when the iteration finally comes to the last item, its value got last assigned with the second-last item, 2
.
add a comment |
The left expression of a for
loop statement gets assigned with each item in the iterable on the right in each iteration, so
for n in a:
print(n)
is just a fancy way of doing:
for i in range(len(a)):
n = a[i]
print(n)
Likewise,
for a[-1] in a:
print(a[-1])
is just a fancy way of doing:
for i in range(len(a)):
a[-1] = a[i]
print(a[-1])
where in each iteration, the last item of a
gets assigned with the next item in a
, so when the iteration finally comes to the last item, its value got last assigned with the second-last item, 2
.
add a comment |
The left expression of a for
loop statement gets assigned with each item in the iterable on the right in each iteration, so
for n in a:
print(n)
is just a fancy way of doing:
for i in range(len(a)):
n = a[i]
print(n)
Likewise,
for a[-1] in a:
print(a[-1])
is just a fancy way of doing:
for i in range(len(a)):
a[-1] = a[i]
print(a[-1])
where in each iteration, the last item of a
gets assigned with the next item in a
, so when the iteration finally comes to the last item, its value got last assigned with the second-last item, 2
.
The left expression of a for
loop statement gets assigned with each item in the iterable on the right in each iteration, so
for n in a:
print(n)
is just a fancy way of doing:
for i in range(len(a)):
n = a[i]
print(n)
Likewise,
for a[-1] in a:
print(a[-1])
is just a fancy way of doing:
for i in range(len(a)):
a[-1] = a[i]
print(a[-1])
where in each iteration, the last item of a
gets assigned with the next item in a
, so when the iteration finally comes to the last item, its value got last assigned with the second-last item, 2
.
answered Apr 12 at 4:08
blhsingblhsing
44.4k51745
44.4k51745
add a comment |
add a comment |
It is an interesting question, and you can understand it by that:
for v in a:
a[-1] = v
print(a[-1])
print(a)
actually a
becomes: [0, 1, 2, 2]
after loop
Output:
0
1
2
2
[0, 1, 2, 2]
I hope that helps you, and comment if you have further questions. : )
add a comment |
It is an interesting question, and you can understand it by that:
for v in a:
a[-1] = v
print(a[-1])
print(a)
actually a
becomes: [0, 1, 2, 2]
after loop
Output:
0
1
2
2
[0, 1, 2, 2]
I hope that helps you, and comment if you have further questions. : )
add a comment |
It is an interesting question, and you can understand it by that:
for v in a:
a[-1] = v
print(a[-1])
print(a)
actually a
becomes: [0, 1, 2, 2]
after loop
Output:
0
1
2
2
[0, 1, 2, 2]
I hope that helps you, and comment if you have further questions. : )
It is an interesting question, and you can understand it by that:
for v in a:
a[-1] = v
print(a[-1])
print(a)
actually a
becomes: [0, 1, 2, 2]
after loop
Output:
0
1
2
2
[0, 1, 2, 2]
I hope that helps you, and comment if you have further questions. : )
edited Apr 12 at 11:10
answered Apr 12 at 4:03
recnacrecnac
2,1172631
2,1172631
add a comment |
add a comment |
The answer by TrebledJ explains the technical reason of why this is possible.
Why would you want to do this though?
Suppose I have an algorithm that operates on an array:
x = np.arange(5)
And I want to test the result of the algorithm using different values of the first index. I can simply skip the first value, reconstructing an array every time:
for i in range(5):
print(np.r_[i, x[1:]].sum())
(np.r_
)
This will create a new array on every iteration, which is not ideal, in particular if the array is large. To reuse the same memory on every iteration, I can rewrite it as:
for i in range(5):
x[0] = i
print(x.sum())
Which is probably clearer than the first version too.
But that is exactly identical to the more compact way to write this:
for x[0] in range(5):
print(x.sum())
all of the above will result in:
10
11
12
13
14
Now this is a trivial "algorithm", but there will be more complicated purposes where one might want to test changing a single (or multiple, but that complicating things due to assignment unpacking) value in an array to a number of values, preferably without copying the entire array. In this case, you may want to use an indexed value in a loop, but be prepared to confuse anyone maintaining your code (including yourself). For this reason, the second version explicitly assigning x[0] = i
is probably preferable, but if you prefer the more compact for x[0] in range(5)
style, this should be a valid use case.
What isnp.r
in the second code block?
– Nathan
Apr 13 at 2:41
@Nathan docs.scipy.org/doc/numpy/reference/generated/numpy.r_.html
– gerrit
Apr 14 at 8:35
Ah, this should benp.r_
; that's why I couldn't find it in mynumpy
.
– Nathan
Apr 14 at 18:53
@Nathan Oops! Corrected.
– gerrit
Apr 14 at 21:09
add a comment |
The answer by TrebledJ explains the technical reason of why this is possible.
Why would you want to do this though?
Suppose I have an algorithm that operates on an array:
x = np.arange(5)
And I want to test the result of the algorithm using different values of the first index. I can simply skip the first value, reconstructing an array every time:
for i in range(5):
print(np.r_[i, x[1:]].sum())
(np.r_
)
This will create a new array on every iteration, which is not ideal, in particular if the array is large. To reuse the same memory on every iteration, I can rewrite it as:
for i in range(5):
x[0] = i
print(x.sum())
Which is probably clearer than the first version too.
But that is exactly identical to the more compact way to write this:
for x[0] in range(5):
print(x.sum())
all of the above will result in:
10
11
12
13
14
Now this is a trivial "algorithm", but there will be more complicated purposes where one might want to test changing a single (or multiple, but that complicating things due to assignment unpacking) value in an array to a number of values, preferably without copying the entire array. In this case, you may want to use an indexed value in a loop, but be prepared to confuse anyone maintaining your code (including yourself). For this reason, the second version explicitly assigning x[0] = i
is probably preferable, but if you prefer the more compact for x[0] in range(5)
style, this should be a valid use case.
What isnp.r
in the second code block?
– Nathan
Apr 13 at 2:41
@Nathan docs.scipy.org/doc/numpy/reference/generated/numpy.r_.html
– gerrit
Apr 14 at 8:35
Ah, this should benp.r_
; that's why I couldn't find it in mynumpy
.
– Nathan
Apr 14 at 18:53
@Nathan Oops! Corrected.
– gerrit
Apr 14 at 21:09
add a comment |
The answer by TrebledJ explains the technical reason of why this is possible.
Why would you want to do this though?
Suppose I have an algorithm that operates on an array:
x = np.arange(5)
And I want to test the result of the algorithm using different values of the first index. I can simply skip the first value, reconstructing an array every time:
for i in range(5):
print(np.r_[i, x[1:]].sum())
(np.r_
)
This will create a new array on every iteration, which is not ideal, in particular if the array is large. To reuse the same memory on every iteration, I can rewrite it as:
for i in range(5):
x[0] = i
print(x.sum())
Which is probably clearer than the first version too.
But that is exactly identical to the more compact way to write this:
for x[0] in range(5):
print(x.sum())
all of the above will result in:
10
11
12
13
14
Now this is a trivial "algorithm", but there will be more complicated purposes where one might want to test changing a single (or multiple, but that complicating things due to assignment unpacking) value in an array to a number of values, preferably without copying the entire array. In this case, you may want to use an indexed value in a loop, but be prepared to confuse anyone maintaining your code (including yourself). For this reason, the second version explicitly assigning x[0] = i
is probably preferable, but if you prefer the more compact for x[0] in range(5)
style, this should be a valid use case.
The answer by TrebledJ explains the technical reason of why this is possible.
Why would you want to do this though?
Suppose I have an algorithm that operates on an array:
x = np.arange(5)
And I want to test the result of the algorithm using different values of the first index. I can simply skip the first value, reconstructing an array every time:
for i in range(5):
print(np.r_[i, x[1:]].sum())
(np.r_
)
This will create a new array on every iteration, which is not ideal, in particular if the array is large. To reuse the same memory on every iteration, I can rewrite it as:
for i in range(5):
x[0] = i
print(x.sum())
Which is probably clearer than the first version too.
But that is exactly identical to the more compact way to write this:
for x[0] in range(5):
print(x.sum())
all of the above will result in:
10
11
12
13
14
Now this is a trivial "algorithm", but there will be more complicated purposes where one might want to test changing a single (or multiple, but that complicating things due to assignment unpacking) value in an array to a number of values, preferably without copying the entire array. In this case, you may want to use an indexed value in a loop, but be prepared to confuse anyone maintaining your code (including yourself). For this reason, the second version explicitly assigning x[0] = i
is probably preferable, but if you prefer the more compact for x[0] in range(5)
style, this should be a valid use case.
edited Apr 14 at 21:09
answered Apr 12 at 11:12
gerritgerrit
7,80654894
7,80654894
What isnp.r
in the second code block?
– Nathan
Apr 13 at 2:41
@Nathan docs.scipy.org/doc/numpy/reference/generated/numpy.r_.html
– gerrit
Apr 14 at 8:35
Ah, this should benp.r_
; that's why I couldn't find it in mynumpy
.
– Nathan
Apr 14 at 18:53
@Nathan Oops! Corrected.
– gerrit
Apr 14 at 21:09
add a comment |
What isnp.r
in the second code block?
– Nathan
Apr 13 at 2:41
@Nathan docs.scipy.org/doc/numpy/reference/generated/numpy.r_.html
– gerrit
Apr 14 at 8:35
Ah, this should benp.r_
; that's why I couldn't find it in mynumpy
.
– Nathan
Apr 14 at 18:53
@Nathan Oops! Corrected.
– gerrit
Apr 14 at 21:09
What is
np.r
in the second code block?– Nathan
Apr 13 at 2:41
What is
np.r
in the second code block?– Nathan
Apr 13 at 2:41
@Nathan docs.scipy.org/doc/numpy/reference/generated/numpy.r_.html
– gerrit
Apr 14 at 8:35
@Nathan docs.scipy.org/doc/numpy/reference/generated/numpy.r_.html
– gerrit
Apr 14 at 8:35
Ah, this should be
np.r_
; that's why I couldn't find it in my numpy
.– Nathan
Apr 14 at 18:53
Ah, this should be
np.r_
; that's why I couldn't find it in my numpy
.– Nathan
Apr 14 at 18:53
@Nathan Oops! Corrected.
– gerrit
Apr 14 at 21:09
@Nathan Oops! Corrected.
– gerrit
Apr 14 at 21:09
add a comment |
a[-1]
refers to the last element of a
, in this case a[3]
. The for
loop is a bit unusual in that it is using this element as the loop variable.
It's not evaluating that element upon loop entry, but rather it is assigning to it on each iteration through the loop.
So first a[-1]
gets set to 0, then 1, then 2. Finally, on the last iteration, the for
loop retrieves a[3]
which at that point is 2
, so the list ends up as [0, 1, 2, 2]
.
A more typical for
loop uses a simple local variable name as the loop variable, e.g. for x ...
. In that case, x
is set to the next value upon each iteration. This case is no different, except that a[-1]
is set to the next value upon each iteration. You don't see this very often, but it's consistent.
How does it actuallygets set to 0, then 1, then 2 and finally 2
? That's the confusing part!
– Amir A. Shabani
Apr 12 at 3:53
1
Agree, I don't really understand by reading this answer
– gameon67
Apr 12 at 3:53
I expected it would be like sayingfor 3 in a: print(a[-1])
(becausea[-1]
was3
at the start of the loop) and give an error, but clearly that's not the case. You're obviously correct about what's happening, but I'm surprised this evaluates this way.
– Nathan
Apr 12 at 3:56
I've expanded the answer to explain more precisely how this works. Hopefully people will understand it this time.
– Tom Karzes
Apr 12 at 4:05
1
"The for loop is a bit unusual in that it is using this element as the loop variable." - This may be where the confusion is coming from. Python isn't using the elementa[-1]
evaluates to, rather, it's using the indexa[-1]
itself. If you rephrase that statement, your answer becomes much more clear.
– Christian Dean
Apr 12 at 4:29
|
show 2 more comments
a[-1]
refers to the last element of a
, in this case a[3]
. The for
loop is a bit unusual in that it is using this element as the loop variable.
It's not evaluating that element upon loop entry, but rather it is assigning to it on each iteration through the loop.
So first a[-1]
gets set to 0, then 1, then 2. Finally, on the last iteration, the for
loop retrieves a[3]
which at that point is 2
, so the list ends up as [0, 1, 2, 2]
.
A more typical for
loop uses a simple local variable name as the loop variable, e.g. for x ...
. In that case, x
is set to the next value upon each iteration. This case is no different, except that a[-1]
is set to the next value upon each iteration. You don't see this very often, but it's consistent.
How does it actuallygets set to 0, then 1, then 2 and finally 2
? That's the confusing part!
– Amir A. Shabani
Apr 12 at 3:53
1
Agree, I don't really understand by reading this answer
– gameon67
Apr 12 at 3:53
I expected it would be like sayingfor 3 in a: print(a[-1])
(becausea[-1]
was3
at the start of the loop) and give an error, but clearly that's not the case. You're obviously correct about what's happening, but I'm surprised this evaluates this way.
– Nathan
Apr 12 at 3:56
I've expanded the answer to explain more precisely how this works. Hopefully people will understand it this time.
– Tom Karzes
Apr 12 at 4:05
1
"The for loop is a bit unusual in that it is using this element as the loop variable." - This may be where the confusion is coming from. Python isn't using the elementa[-1]
evaluates to, rather, it's using the indexa[-1]
itself. If you rephrase that statement, your answer becomes much more clear.
– Christian Dean
Apr 12 at 4:29
|
show 2 more comments
a[-1]
refers to the last element of a
, in this case a[3]
. The for
loop is a bit unusual in that it is using this element as the loop variable.
It's not evaluating that element upon loop entry, but rather it is assigning to it on each iteration through the loop.
So first a[-1]
gets set to 0, then 1, then 2. Finally, on the last iteration, the for
loop retrieves a[3]
which at that point is 2
, so the list ends up as [0, 1, 2, 2]
.
A more typical for
loop uses a simple local variable name as the loop variable, e.g. for x ...
. In that case, x
is set to the next value upon each iteration. This case is no different, except that a[-1]
is set to the next value upon each iteration. You don't see this very often, but it's consistent.
a[-1]
refers to the last element of a
, in this case a[3]
. The for
loop is a bit unusual in that it is using this element as the loop variable.
It's not evaluating that element upon loop entry, but rather it is assigning to it on each iteration through the loop.
So first a[-1]
gets set to 0, then 1, then 2. Finally, on the last iteration, the for
loop retrieves a[3]
which at that point is 2
, so the list ends up as [0, 1, 2, 2]
.
A more typical for
loop uses a simple local variable name as the loop variable, e.g. for x ...
. In that case, x
is set to the next value upon each iteration. This case is no different, except that a[-1]
is set to the next value upon each iteration. You don't see this very often, but it's consistent.
edited Apr 12 at 4:47
answered Apr 12 at 3:51
Tom KarzesTom Karzes
11.3k1926
11.3k1926
How does it actuallygets set to 0, then 1, then 2 and finally 2
? That's the confusing part!
– Amir A. Shabani
Apr 12 at 3:53
1
Agree, I don't really understand by reading this answer
– gameon67
Apr 12 at 3:53
I expected it would be like sayingfor 3 in a: print(a[-1])
(becausea[-1]
was3
at the start of the loop) and give an error, but clearly that's not the case. You're obviously correct about what's happening, but I'm surprised this evaluates this way.
– Nathan
Apr 12 at 3:56
I've expanded the answer to explain more precisely how this works. Hopefully people will understand it this time.
– Tom Karzes
Apr 12 at 4:05
1
"The for loop is a bit unusual in that it is using this element as the loop variable." - This may be where the confusion is coming from. Python isn't using the elementa[-1]
evaluates to, rather, it's using the indexa[-1]
itself. If you rephrase that statement, your answer becomes much more clear.
– Christian Dean
Apr 12 at 4:29
|
show 2 more comments
How does it actuallygets set to 0, then 1, then 2 and finally 2
? That's the confusing part!
– Amir A. Shabani
Apr 12 at 3:53
1
Agree, I don't really understand by reading this answer
– gameon67
Apr 12 at 3:53
I expected it would be like sayingfor 3 in a: print(a[-1])
(becausea[-1]
was3
at the start of the loop) and give an error, but clearly that's not the case. You're obviously correct about what's happening, but I'm surprised this evaluates this way.
– Nathan
Apr 12 at 3:56
I've expanded the answer to explain more precisely how this works. Hopefully people will understand it this time.
– Tom Karzes
Apr 12 at 4:05
1
"The for loop is a bit unusual in that it is using this element as the loop variable." - This may be where the confusion is coming from. Python isn't using the elementa[-1]
evaluates to, rather, it's using the indexa[-1]
itself. If you rephrase that statement, your answer becomes much more clear.
– Christian Dean
Apr 12 at 4:29
How does it actually
gets set to 0, then 1, then 2 and finally 2
? That's the confusing part!– Amir A. Shabani
Apr 12 at 3:53
How does it actually
gets set to 0, then 1, then 2 and finally 2
? That's the confusing part!– Amir A. Shabani
Apr 12 at 3:53
1
1
Agree, I don't really understand by reading this answer
– gameon67
Apr 12 at 3:53
Agree, I don't really understand by reading this answer
– gameon67
Apr 12 at 3:53
I expected it would be like saying
for 3 in a: print(a[-1])
(because a[-1]
was 3
at the start of the loop) and give an error, but clearly that's not the case. You're obviously correct about what's happening, but I'm surprised this evaluates this way.– Nathan
Apr 12 at 3:56
I expected it would be like saying
for 3 in a: print(a[-1])
(because a[-1]
was 3
at the start of the loop) and give an error, but clearly that's not the case. You're obviously correct about what's happening, but I'm surprised this evaluates this way.– Nathan
Apr 12 at 3:56
I've expanded the answer to explain more precisely how this works. Hopefully people will understand it this time.
– Tom Karzes
Apr 12 at 4:05
I've expanded the answer to explain more precisely how this works. Hopefully people will understand it this time.
– Tom Karzes
Apr 12 at 4:05
1
1
"The for loop is a bit unusual in that it is using this element as the loop variable." - This may be where the confusion is coming from. Python isn't using the element
a[-1]
evaluates to, rather, it's using the index a[-1]
itself. If you rephrase that statement, your answer becomes much more clear.– Christian Dean
Apr 12 at 4:29
"The for loop is a bit unusual in that it is using this element as the loop variable." - This may be where the confusion is coming from. Python isn't using the element
a[-1]
evaluates to, rather, it's using the index a[-1]
itself. If you rephrase that statement, your answer becomes much more clear.– Christian Dean
Apr 12 at 4:29
|
show 2 more comments
28
Somehow this question looks like a bad and newbie question, but I don't get the logic either lol
– gameon67
Apr 12 at 3:55
13
I don't know why you would ever want to do this, but now I know you can
– Nathan
Apr 12 at 3:57
over the iteration only the last time a[-1] gets its value, all other times python treats as iteration variable.
– Arun Augustine
Apr 12 at 4:01
41
This would be a great question for an awful coding interview
– Nathan
Apr 12 at 4:04
2
Btw OP, nice first question!
– Christian Dean
Apr 12 at 23:21