It's somewhat intuitive if you refactor as a while loop
a = [0, 1, 2, 3]
for a[-1] in a:
print(a[-1])
becomes
a = [0, 1, 2, 3]
while i < len(a):
e = a[i]
a[-1] = e # the last element is constantly being overwritten
print(a[-1]) # same as a[i]
So the value of a over time is the following
[0, 1, 2, 3] # original
[0, 1, 2, 0] # prints 0
|--------^
[0, 1, 2, 1] # prints 1
|-----^
[0, 1, 2, 2] # prints 2
|--^
[0, 1, 2, 2] # prints 2
|^
Similarly in the second case
for a[0] in a:
print(a[0])
becomes
a = [0, 1, 2, 3]
while i < len(a):
e = a[i]
a[0] = e # the first element is constantly being overwritten
print(a[0]) # same as a[i]
and the value of a is
[0, 1, 2, 3] # original
[0, 1, 2, 3] # prints 0
^|
[1, 1, 2, 3] # prints 1
^--|
[2, 1, 2, 2] # prints 2
^-----|
[3, 1, 2, 3] # prints 3
^--------|