Analyzing the behaviour of Python function slice
Last Friday, I was sitting in one of the good coffee shops in Bangalore with my friend. Coffee and discussion is the best combination to release stress. It was looking like a perfect Friday evening until my friend was struck by an idea of asking me a question.
“Let me conduct a quiz.” he said, interrupting our conversation.
“A quiz? Quiz on what?”, I asked.
“On programming”, he said
“What is the level of difficulty then?” I said.
Asking the level of difficulty is important. I never invest my efforts in solving something easy. If he had said easy, I would have ignored to answer, but he said “It is a bit difficult, but not that difficult. I gave a wrong answer to this question in my last interview.”
There was no reason to go back from here. Taking some deep breaths I said, “Please go ahead.”
He stood up, took a tissue paper from a nearby counter and scratched below code on it. 1And he asked me by pointing towards that code, “What will be the output of this code?”
def my_function():
l = [0, 1, 2]
print(l[30:])
my_function()
Now it was my turn to give the answer. I looked at the code and tried parsing it in my head — line by line. In my mind, I observed the first line. It was defining a function which seems to be correct. I moved my eyes to the next line. It was defining a variable l of type listand assigning values ranging from 0 to 2. Even this wasn’t looking problematic. So I forwarded to the next line where it was trying to print that variable l by slicing it from starting value 30 to the infinity.
“Well, the start value is 30 which is greater than the length of the list. This should raise an IndexError” I said in my mind. I was about to speak an answer, but suddenly Devil of me flashed.
“It is less than a banana job my dear,” the Devil said to me, “You should take a little advantage of this opportunity my boy.”
Because things were looking in my control, I shook my hands with the Devil.
I said to my friend, “How about betting for some real values?”
Going closer I spoke, “If I answer correctly, You will pay the bill and If I am wrong, This will be a treat from my side.”
He thought for a while and nodded. Now it was my turn to unveil the cards.
I said in a strong voice, “It will raise an IndexError.” And shifted my focus towards the chocolate.
He starred my face for a second and spoke, “Okay. Are you sure about this?”.
This was the hint he gave. I should have taken another shot here. What happens next become a lesson for me.
I said with a flat face, “Yes I am.”
With my answer, he instantly opened his backpack, took his Laptop out and typed the code which he wrote on that tissue.
When I stopped hearing a sound of typing I yelled, “So did I win?”
He turned his laptop towards me and exclaimed, “Not at all!”
When I focused on the screen, the interpreter was printing []. Damn! I lost the bet. Why the hell slice is returning an empty list even when we are trying to slice it with a value which is greater than the length of it! It was surely looking unpythonic behavior. I paid whatever the bill amount was. Entire evening this question was all roaming my mind. After coming home, I decided to justify reasons for returning an empty list instead of raising an IndexError from a slice.
Below are a few reasons justifying such behavior of slice function. I am sharing this with you so that you don’t lose a bet with your friend :) For those who haven’t used slice anytime in their life, I advise to read this tutorial. Reading this guide for understanding how a slice function converts the input values. Especially rule number 3 and 4 referenced there.
Reason number one:
Python lists are more commonly used in iterations. Consider below example:
numbers = [0, 1, 2, 3]
for number in numbers[30:]:
print(number)
If slice was raising an IndexError, then the above code would have to written like this written like like this
numbers = [0, 1, 2, 3]
try:
for number in numbers[30:]:
print(numbers)
except IndexError:
pass
Or in another way below is also looking reasonable
numbers = [0, 1, 2, 3]
start = 30
if start < len(numbers):
for number in numbers[start:]:
print(number)
Both the approaches are looking little lengthy by an obvious reason. And that reason is to prevent executing loop if there are no elements in it. When we observe the behavior of slice called at for in, it makes sense to return an empty list instead of raising an IndexError.
I am not able to find further reasons to return an empty list instead of raising the IndexError. But I am sure, there will be. If you know any other potential reasons for such behavior of slice, please drop me a mail at jaysinhp at gmail dot com or contact me over Twitter @jaysinhp. I will update the reasons at this post and give credits to you. Thanks for reading this post.
Proofreaders: Geoffrey Sneddon, Elijah, Mahendra Yadav, Dhavan Vaidya,
- I am using the word “Scratch” because that tissue paper was such a thin that writing by a ballpen torn it. ?