Why does this result in a traceback?

Hi!

I’m doing this project https://www.codecademy.com/courses/learn-python-3/projects/carlys-clippers.

I used this code:

hairstyles = ["bouffant", "pixie", "dreadlocks", "crew", "bowl", "bob", "mohawk", "flattop"]

prices = [30, 25, 40, 20, 20, 35, 50, 35]

last_week = [2, 3, 5, 8, 4, 4, 6, 2]

new_prices = [price - 5 for price in prices]

cuts_under_30 = [hairstyles[i] for i in hairstyles if new_prices[i] < 30]

print(cuts_under_30)

Which resulted in:

Traceback (most recent call last):
File “script.py”, line 23, in
cuts_under_30 = [hairstyles[i] for i in hairstyles if new_prices[i] < 30]
File “script.py”, line 23, in
cuts_under_30 = [hairstyles[i] for i in hairstyles if new_prices[i] < 30]
TypeError: list indices must be integers or slices, not str

I fixed the code by using this


cuts_under_30 = [hairstyles[i] for i in range(len(new_prices)) if new_prices[i] < 30]

instead of this

cuts_under_30 = [hairstyles[i] for i in hairstyles if new_prices[i] < 30]

What was wrong with the original code?
Thanks!

Hi,

The error message is a big clue as to the nature of the issue.
In a for-loop or list comprehension, the default object that is iterated is whatever is in the list. So something like [fruit for fruit in fruits] will have fruit change values to things like 'banana', 'orange', 'pear'. So later if you wanted to access the same list, you can’t access it by saying fruits[‘banana’].

That’s why if you envision needing to access indices, it’s useful to iterate through the indices of the list (which are generated by the range technique you used). index for index in range(len(fruits)) will now iterate indices 0 through whatever the length of your list is. Now you have accurately type fruits[index] (because index will be a number within the range of your list) and have it refer to a particular index in the list.

There’s a workaround to this (though it’s not always good to use it so I mention it last). You can ask for the index of the item value you’re iterating. But that can run into trouble if there are duplicates.

Example:

>>> fruits = ["banana", "pear", "orange"]
>>> fruits.index("pear")
1

Extra side note: If this were a dictionary it’d be the other way around, because dictionary values are all unique (by nature). That means you can’t access them by index but need the actual “key” of the name.

Example:

>>> fruits = {"banana":{"price": 1},"pear":{"price":4},"orange":{"price":2.5}}
>>> fruits['banana']
{'price': 1}
>>> fruits['pear']['price']
4
4 Likes

Thanks for taking the time to answer.

I did not understand the explanation (I’m really new at coding). In a regular for-loop iterating through a list, you do not need to use the index numbers. You can just specify the list, and Python understands to iterate through the indices of the list. What is different here?

I’ll make a subtle distinction, in a for-loop that’s written for i in list, the loop iterates through the value of the indices, not the indices themselves.

You can play in the terminal to get a better understanding, I’ve typed in a few lines that hopefully gives you an idea of how you can further explore:

>>> test_list = ["book","magazine","sheet music"]
>>> for item in test_list:
...     print(item)
... 
book
magazine
sheet music
>>> for i in range(len(test_list)):
...     print(i)
... 
0
1
2
>>> test_list["book"]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list indices must be integers or slices, not str
>>> test_list[0]
'book'

1 Like

Now I understand. Thanks!

1 Like