Making a for loop more Pythonic

We’ll start with a piece of Python code that is correct but which can be improved. The code can be more concise (in fact, some of the code does not do anything). We’ll see at the end how to do the same thing using list comprehension. Using list comprehension instead of an explicit for loop probably is not much more efficient, but it is much more Pythonic.

Zoom recording from class on 1/4/2022

Starter code

Try to read this code, step by step, and figure out what it does.

my_list = [3.14,10,-4,3,5,5,-1,20.3,14,0]
new_list = []
for i in range(0,len(my_list)):
    if my_list[i] > 4:
        new_list.append(my_list[i])
    else:
        new_list = new_list
new_list
[10, 5, 5, 20.3, 14]

Revision 1

Remove the else statement, which wasn’t doing anything.

new_list = []
for i in range(0,len(my_list)):
    if my_list[i] > 4:
        new_list.append(my_list[i])
new_list
[10, 5, 5, 20.3, 14]

Revision 2

Iterate through the elements in my_list, not the numbers from 0 to 9.

new_list = []
for x in my_list:
    if x > 4:
        new_list.append(x)
new_list
[10, 5, 5, 20.3, 14]

Final version

Using list comprehension.

new_list = [x for x in my_list if x > 4]
new_list
[10, 5, 5, 20.3, 14]

Miscellaneous examples

The above portion is what you should focus on, but we briefly touched on lots of quick examples. Here are some of them.

for loops are the standard way to repeat something.

for i in range(6):
    print("hi")
hi
hi
hi
hi
hi
hi

Notice that the right endpoint is not included. For example, in the following, we never set i equal to 5.

for i in range(0,5):
    print(i)
0
1
2
3
4

The significance of indentation after a for loop:

for i in range(0,5):
    print("i")
print(i)
i
i
i
i
i
4
for i in range(0,5):
    print("i")
    print(i)
i
0
i
1
i
2
i
3
i
4

You don’t need the zero: range(0,5) is the same as range(5).

for i in range(5):
    print(i)
0
1
2
3
4

This might remind you of the colon operator from Matlab, although the order is rearranged (and in Matlab, the right endpoint is included).

for i in range(0,20,3):
    print(i)
0
3
6
9
12
15
18
for i in range(1,20,3):
    print(i)
1
4
7
10
13
16
19

Here is the error message that shows up if you forget the colon:

for i in range(0,5)
    print("i")
    print(i)
  File "/var/folders/8j/gshrlmtn7dg4qtztj4d4t_w40000gn/T/ipykernel_5597/451835478.py", line 1
    for i in range(0,5)
                       ^
SyntaxError: invalid syntax

range is sort of like a list, but it’s not the same data type.

y = range(5)
type(y)
range
z = range(0,20,3)
type(z)
range

Converting from a range object to a list object:

list(z)
[0, 3, 6, 9, 12, 15, 18]

Converting from a str (string) object to a list object.

list("chris")
['c', 'h', 'r', 'i', 's']

As one example of an advantage of range over list, you can create huge range objects that would never fit in the computer’s memory if literally made as a list. Also, notice that you do not make exponents in Python using the caret symbol ^; instead you make exponents using **.

z = range(0,10**20)

Practice with append.

z = list("chris")
z.append(15)
z
['c', 'h', 'r', 'i', 's', 15]
help(z.append)
Help on built-in function append:

append(object, /) method of builtins.list instance
    Append object to the end of the list.

One way to insert something into a list at a specific position (as opposed to append which inserts it at the end). You can see again here that right endpoints are not included.

z[0:3]+["hello"]+z[3:]
['c', 'h', 'r', 'hello', 'i', 's', 15]

Another way, using insert.

help(z.insert)
Help on built-in function insert:

insert(index, object, /) method of builtins.list instance
    Insert object before index.
z.insert(3,"goodbye")
z
['c', 'h', 'r', 'goodbye', 'i', 's', 15]

Notice that sometimes nothing gets displayed after a command, like the following. That is a clue that possibly the original object got changed.

z.append(12)
z
['c', 'h', 'r', 'goodbye', 'i', 's', 15, 12]

Practice with list comprehension.

my_list
[3.14, 10, -4, 3, 5, 5, -1, 20.3, 14, 0]
new_list = [x for x in my_list if x > 4]
new_list
[10, 5, 5, 20.3, 14]
[x if x > 4 else 100 for x in my_list]
[100, 10, 100, 100, 5, 5, 100, 20.3, 14, 100]
[x+1 if x > 4 else x-1 for x in my_list]
[2.14, 11, -5, 2, 6, 6, -2, 21.3, 15, -1]
[x+1 for x in my_list if x > 4]
[11, 6, 6, 21.3, 15]

Python is pretty strict about where you put the if. Notice the subtle differences between the following.

[x if x > 4 else 100 for x in my_list]
[100, 10, 100, 100, 5, 5, 100, 20.3, 14, 100]
[x for x in my_list if x > 4 else 100]
  File "/var/folders/8j/gshrlmtn7dg4qtztj4d4t_w40000gn/T/ipykernel_5597/3995715388.py", line 1
    [x for x in my_list if x > 4 else 100]
                                 ^
SyntaxError: invalid syntax

And in the following, which seems almost like the opposite.

[x+1 for x in my_list if x > 4]
[11, 6, 6, 21.3, 15]
[x+1 if x > 4 for x in my_list]
  File "/var/folders/8j/gshrlmtn7dg4qtztj4d4t_w40000gn/T/ipykernel_5597/1415182719.py", line 1
    [x+1 if x > 4 for x in my_list]
                  ^
SyntaxError: invalid syntax

Another example of list comprehension. Here we get the words in the sentence that start with the letter h.

text = "hello there how are you doing?  I am fine."
list_of_words = text.split()
list_of_words
['hello', 'there', 'how', 'are', 'you', 'doing?', 'I', 'am', 'fine.']
[word for word in list_of_words if word[0] == "h"]
['hello', 'how']

There’s nothing special about the use of the word word, it could be any variable name.

[i for i in list_of_words if i[0] == "h"]
['hello', 'how']

Notice that numbering in Python starts at 0.

list_of_words[0]
'hello'
list_of_words[1]
'there'
list_of_words[len(list_of_words)]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
/var/folders/8j/gshrlmtn7dg4qtztj4d4t_w40000gn/T/ipykernel_5597/2992339649.py in <module>
----> 1 list_of_words[len(list_of_words)]

IndexError: list index out of range