07 Cultivating Basic Conditions and Loops

07 Cultivating Basic Conditions and Loops #

Hello, I’m Jingxiao.

In the previous sections, we have learned a series of basic data types in Python, such as lists, tuples, dictionaries, sets, and strings. However, how can we connect these individual basic data structures to form a beautiful piece of code? This is what we are going to discuss today – “Conditions and Loops.”

I like to call “Conditions and Loops” the basic skills in programming. Why do I call them basic skills? Because they control the logic of the code and can be said to be the central system of a program. If we compare writing a program to building a house, then conditions and loops are the foundation of the house, and everything else is built on this foundation.

It is not an exaggeration to say that writing clean and readable code with conditions and loops is crucial for improving the overall quality of a program.

Conditional Statements #

First of all, let’s take a look at conditional statements in Python. It’s very simple to use. For example, if I want to represent the function y=|x|, the corresponding code would be:

# y = |x|
if x < 0:
    y = -x
else:
    y = x

Unlike other languages, we cannot use parentheses in conditional statements, like the format below:

if (x < 0)

However, it’s important to note that a colon (:) must be added at the end of the conditional statement. This is a specific syntax requirement in Python.

Since Python doesn’t support a switch statement, when there are multiple conditions to be checked, we need to use elif to achieve the same result. The syntax is as follows:

if condition_1:
    statement_1
elif condition_2:
    statement_2
...
elif condition_i:
    statement_i
else:
    statement_n

The whole conditional statement is executed in order. If a condition is satisfied, for example, when condition_i is satisfied, the execution will stop after statement_i is executed. It won’t continue to execute the following statements. This statement is commonly used in practice, like in the example below.

In real-world scenarios, we often use IDs to represent the attributes of an object and then make conditional statements based on that and output the result. For example, in the field of integrity, the degree of violence or pornography in a movie is usually represented by 0, 1, or 2. Among them, 0 represents the highest level, which is red; 1 is the second level, which is yellow; and 2 means there are no quality issues, so it belongs to green.

If given an ID and asked to output the quality rating of a movie, the code would be:

if id == 0:
    print('red')
elif id == 1:
    print('yellow')
else:
    print('green')

However, keep in mind that if statements can be used independently, but elif and else must be paired with if.

Furthermore, when making conditional statements, many people like to omit the condition. For example, they may write it like this:

if s: # s is a string
    ...
if l: # l is a list
    ...
if i: # i is an int
    ...
...

I have summarized some common usages of omitting the condition:

But remember, when writing actual code, we encourage explicit condition statements for everything except boolean type data. For example, when checking if an integer is not equal to 0, it’s better to write the condition explicitly:

if i != 0:
    ...

Instead of just writing the variable name:

if i:
    ...

Loop Statements #

After talking about conditional statements, let’s move on to loop statements. The so-called loop, as the name suggests, is essentially to traverse the elements in a collection. Like other languages, loops in Python are generally implemented using for loops and while loops.

For example, if we have a list and need to iterate through all the elements and print them out, the code would be as follows:

l = [1, 2, 3, 4]
for item in l:
    print(item)
1
2
3
4

You see, it’s quite simple, right?

In fact, as long as the data structure in Python is iterable (like lists, sets, etc.), we can iterate through them using the following method:

for item in <iterable>:
    ...

Here, I need to emphasize dictionaries separately. Only the keys of a dictionary are iterable. If we want to iterate through its values or key-value pairs, we need to use the built-in functions values() or items(). Specifically, values() returns the collection of values in the dictionary, while items() returns the collection of key-value pairs.

d = {'name': 'jason', 'dob': '2000-01-01', 'gender': 'male'}
for k in d: # iterate through the keys of the dictionary
    print(k)
name
dob
gender

for v in d.values(): # iterate through the values of the dictionary
    print(v)
jason
2000-01-01
male

for k, v in d.items(): # iterate through the key-value pairs of the dictionary
    print('key: {}, value: {}'.format(k, v))
key: name, value: jason
key: dob, value: 2000-01-01
key: gender, value: male

By the way, you may ask if it is possible to iterate through elements using their indices. Of course, it is possible. In fact, this kind of situation is quite common in actual work, and sometimes we need to do some conditional judgment based on the indices.

We usually use the range() function to get the indices and then iterate through the elements in the collection. For example, the following code iterates through the elements in a list and prints them out when the index is less than 5:

l = [1, 2, 3, 4, 5, 6, 7]
for index in range(0, len(l)):
    if index < 5:
        print(l[index])
        
1
2
3
4
5

When we need both the index and the element at the same time, there is a more concise way to achieve this using the built-in function enumerate(). It allows us to iterate through the collection, returning not only each element but also its corresponding index. This way, the example above can be written as follows:

l = [1, 2, 3, 4, 5, 6, 7]
for index, item in enumerate(l):
    if index < 5:
        print(item)
      
1
2
3
4
5

In loop statements, we often use continue and break together. The keyword continue means that it skips the current iteration of the loop and continues with the next iteration, while break means completely exiting the current loop. Properly adding continue and break in loops can often make the program more concise and readable.

For example, given two dictionaries, one mapping product names to prices, and another mapping product names to a list of colors. We want to find all combinations of product names and colors where the price is less than 1000 and the color is not red. If we don’t use continue, the code would look like this:

# name_price: mapping dictionary from product name (str) to price (int)
# name_color: mapping dictionary from product name (str) to a list of colors (list of str)
for name, price in name_price.items():
    if price < 1000:
        if name in name_color:
            for color in name_color[name]:
                if color != 'red':
                    print('name: {}, color: {}'.format(name, color))
        else:
            print('name: {}, color: {}'.format(name, 'None'))

With the addition of continue, the code becomes much clearer:

# name_price: a mapping dictionary from product name (str) to price (int)
# name_color: a mapping dictionary from product name (str) to color (list of str)
for name, price in name_price.items():
    if price >= 1000:
        continue
    if name not in name_color:
        print('name: {}, color: {}'.format(name, 'None'))
        continue
    for color in name_color[name]:
        if color == 'red':
            continue
        print('name: {}, color: {}'.format(name, color))

We can see that in the first version, from the beginning until printing the product names and colors that meet the conditions, there are a total of 5 levels of nested for or if statements. However, in the second version with the addition of continue, there are only 3 levels of nesting.

Clearly, if there are nested statements within nested statements in the code, the code becomes very redundant and difficult to read, and it is not conducive for debugging and modification in the future. Therefore, we should try to avoid such situations with multiple levels of nesting.

Earlier, we discussed for loops, and the same principles apply to while loops. They indicate that the operations within the loop are repeated as long as a condition is met until the condition is no longer met, and then the loop is exited.

while condition:
    ...

Often, for loops and while loops can be converted into each other. For example, if we want to iterate over a list, we can also accomplish that with a while loop:

l = [1, 2, 3, 4]
index = 0
while index < len(l):
    print(l[index])
    index += 1

So, what is the difference between their use cases?

Generally speaking, if you just need to iterate over a known collection, find elements that meet certain conditions, and perform corresponding operations, for loops are more concise. But if you need to repeatedly perform certain operations until a specific condition is met and there is no specific collection to iterate over, you would generally use a while loop.

For example, in an interactive question and answer system, where the user inputs text and the system responds accordingly, we would generally use a while loop. The code might look something like this:

while True:
    try:
        text = input('Please enter your questions, enter "q" to exit')
        if text == 'q':
            print('Exit system')
            break
        ...
        ...
        print(response)
    except Exception as err:
        print('Encountered error: {}'.format(err))
        break 

It is also important to note the efficiency of for loops and while loops. For example, consider the following while loop:

i = 0
while i < 1000000:
    i += 1

And its equivalent for loop:

for i in range(0, 1000000):
    pass

Which one is more efficient?

It is worth noting that the range() function is directly implemented in C language and is very fast when called. On the other hand, in the while loop, the operation i += 1 is indirectly called through Python’s interpreter and involves object creation and deletion (since i is an integer and is immutable, i += 1 is equivalent to i = new int(i + 1)). Therefore, it is clear that the for loop is more efficient.

Reusing Conditionals and Loops #

The previous two sections discussed some basic operations of conditionals and loops. Now, let’s focus on their advanced operations to make our programs more concise and efficient.

When reading code, you often find many operations that combine conditionals and loops into a single line, such as:

expression1 if condition else expression2 for item in iterable

If we break down this expression, it is actually equivalent to the following nested structure:

for item in iterable:
    if condition:
        expression1
    else:
        expression2

If there is no else statement, it needs to be written as:

expression for item in iterable if condition

For example, if we want to plot the graph of the function y = 2*|x| + 5 and given a set of data points for x, we need to calculate the corresponding set of data points for y. With just one line of code, we can easily solve this problem:

y = [value * 2 + 5 if value > 0 else -value * 2 + 5 for value in x]

Another common scenario when processing strings in a file is to read a complete sentence line by line, split the words by commas, remove leading and trailing whitespace, and filter out words with a length less than or equal to 3. This can also be expressed concisely in one line:

text = ' Today,  is, Sunday'
text_list = [s.strip() for s in text.split(',') if len(s.strip()) > 3]
print(text_list)
['Today', 'Sunday']

Of course, such reuse is not limited to just one loop. For example, given two lists x and y, we need to return a list of tuples consisting of all pairs of elements from x and y, excluding pairs where the elements are equal. You can easily express it like this:

[(xx, yy) for xx in x for yy in y if xx != yy]

This expression is equivalent to:

l = []
for xx in x:
    for yy in y:
        if xx != yy:
            l.append((xx, yy))

Once you become proficient with this syntax, you will find it very convenient. However, if you encounter complex logic for reuse, you may find it difficult to understand and prone to error when written in a single line. In that case, expressing it in a normal format is also a good practice and choice.

Summary #

In today’s class, we have learned the basic concepts, advanced usage, and relevant applications of conditionals and loops. Here, I would like to emphasize a few common mistakes.

  • In conditional statements, if can be used alone, but elif and else must be used together with if. Additionally, it is preferable to explicitly show the conditions for the if statement, apart from boolean types.

  • In for loops, if you need to access both the index and the element, you can use the enumerate() function to simplify your code.

  • When writing conditionals and loops, it is important to make proper use of continue or break to avoid complex nesting.

  • Pay attention to reusing conditions and loops. Simple tasks can often be completed in a single line, greatly improving code quality and efficiency.

Thinking Question #

Finally, I have a thinking question for you. Given the two lists attributes and values below, it is required to output a dictionary for each sublist value in values based on the corresponding keys in attributes, and finally return a list of dictionaries.

attributes = ['name', 'dob', 'gender']
values = [['jason', '2000-01-01', 'male'],
['mike', '1999-01-01', 'male'],
['nancy', '2001-02-01', 'female']
]

# expected output:
[{'name': 'jason', 'dob': '2000-01-01', 'gender': 'male'},
{'name': 'mike', 'dob': '1999-01-01', 'gender': 'male'},
{'name': 'nancy', 'dob': '2001-02-01', 'gender': 'female'}]

Can you implement this functionality using both a single-line and multi-line conditional loop statement?

Feel free to write down your answers in the comments section, along with your thoughts and questions from today’s study. You’re also welcome to share this article with your colleagues and friends.