Earlier today, a student and I were exploring academic Python challenges on a website called Finxter, and came across an interesting problem:
a = [1, 2, 3] a[2:4] = [4, 5, 6] print(a)
I, unexpectedly, observed that this code results in
[1, 2, 4, 5, 6] being printed out, but it would not have been unexpected had I been properly educated in Python myself.
As a Computer Science instructor and tutor, I have assisted in figuring out uncertainties in student code across a variety of languages and situations. Often, one encounters code that they might not have seen before, or code written in a way or with a context that is unexpected.
I've been writing Python for over 6 years now, and had not once ever considered putting a list-slice to the left of the equals sign!
So, considering the unexpectedness of this code, what would I have intuited that it should have yielded instead?
Well, my first thought was that the length of the slice was longer than the length of the list, so there should have been an error of some sort indicating to me that I was dabbling in dangerous territory
Coming from a strong background in C (I only call it "strong" in that it is a language I have spent enough time with that it impacts how I think about the construction of programs -- an approach that has it's own pros and cons, more on that in a future post), my thinking was that by stepping outside of the bounds of the list in the slice, that I was violating something and perhaps an
Exception should be thrown. In a language like C, there are no protections, so attempting to access a memory location in this way may or may not actually crash the program, but certainly exposes it to doing so.
But no, this is not what happens.
Python happy doesn't care what comes after the
: in a list slice as an l-value.
a = [1, 2, 3] a[2:4] = [4, 5, 6] # this is equal to a[2:] = [4, 5, 6] # this print(a) # [1, 2, 4, 5, 6]
To observe the difference between list-slice as l-value and simply assigning a value to a location:
a = [1, 2, 3] a[2:] = [4, 5, 6] # list-slice as l-value print(a) # [1, 2, 4, 5, 6] a = [1, 2, 3] a = [4, 5, 6] # regular assignment print(a) # [1, 2, [4, 5, 6]]
Ok, so, big deal, why does this matter?
I asked myself "I wonder what happens if I use this technique to assign values past the last index in the list?"
a = [1, 2, 3] a[9001:] = [4, 5, 6] print(a) # [1, 2, 3, 4, 5, 6]
This is effectively equivalent to appending items to the list, but does so in one statement.
a = [1, 2, 3] a.append(4) a.append(5) a.append(6) a = [1, 2, 3] a[7777:] = [4, 5, 6]
I just wanted to share the discovery of a quirk in the Python language that is new not only to me, but also my student as well :)
Happy Thanksgiving everyone!
- Get one month FREE of NordVPN
- Get $100 for your next Virtual Private Server at Vultr Hosting!
- Get $10 FREE when you join Coinbase!