Learn to be Pythonic: List of Multiples of 3
Tags: python, beginners
Published: 2024-02-26 06:59:14

# Introduction

One of the best ways to learn a programming language is by solving the same challenge with different approaches. Learning to be more [Pythonic](https://en.wiktionary.org/wiki/Pythonic#English) is no exception. We will solve simple Python programming challenges going from naive and straightforward ways to more Pythonic and smarter approaches. By doing so, we can gain a deeper understanding of what Python offers out of the box and appreciate the flexibility of this beautiful language.


# Problem Statement

> Create a list of multiples of 3 from 0 to 15 (inclusive)
> 


# Solutions

## Brute Force

The simplest solution (almost like cheating) is just initializing a list with multiples of 3.

```python
ans = [0, 3, 6, 9, 12, 15]
```

## While Loop with Break

Observing the problem statement, we induced that we can go through each integer `i` from 0 to 15, and check whether it is a multiple of 3 (`i % 3 == 0`). A naive implementation is to use an infinite loop (`while True`) and `break` out when `i` is greater than 15.

(This is inspired by a Python quiz question.)

```python
N = 15

ans = []
i = 0
while True:
    if i % 3 == 0:
        ans.append(i)
    if i > N:
        break
    i += 1
```

## While Loop with Condition

A slight improvement to the above solution is to make use of the condition of the `while` loop: loop whenever `i < N + 1`.

```python
N = 15

ans = []
i = 0
while i < N + 1:
    if i % 3 == 0:
        ans.append(i)
    i += 1
```

## While Loop Incremented by Steps

You may have observed that, for this particular challenge, we can reduce the number of steps by incrementing `i` by 3. This also eliminates the check for modulo 3.

```python
N = 15

ans = []
i = 0
while i < N + 1:
    ans.append(i)
    i += 3
```

## For Loop

In fundamental computer science, when the number of iterations is known in advance, a `for` loop is more appropriate. A very useful iterator in Python for this use case is `range`.

```python
N = 15

ans = []
for i in range(N + 1):
    if i % 3 == 0:
        ans.append(i)
```

This is cleaner and more expressive than the `while` loop.

## List Comprehension

To further refactor the solution, we turn to a feature of Python called “[list comprehension](https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions)”. It provides a concise way to create a list from an iterator. This is when the solution starts to be more “Pythonic”.

```python
N = 15

ans = [i for i in range(N + 1) if i % 3]
```

## Filter

An alternative solution without the `for` loop is using the `filter` function. It is handy for filtering an iterator through a test function. In this example, the test function is constructed with `lambda`.

```python
N = 15

ans = list(filter(lambda x: x % 3 == 0, range(N + 1)))
```

## Filter with Predefined Function

The `lambda` function is convenient but lacks context and compromises readability. A better version of the `filter` solution is to predefine the test function.

```python
N = 15

def is_mul_3(x):
    return x % 3 == 0

ans = list(filter(is_mul_3, range(N + 1)))
```

## Range with Steps

In a previous solution, we optimized the loop by reducing the number of iterations by incrementing the counter by a step of 3. This is possible with the `range` function by taking advantage of its optional third argument, `step`. It tells `range` to increment the counter by the `step` amount. As such, we can remove the `filter` altogether.

```python
N = 15

ans = list(range(0, N + 1, 3))
```


# Conclusion

By finding different ways to solve a simple problem, we have already touched on the key Python concepts of `list`, `while`, `break`, `for`, `range`, `filter`, `lambda`, and list comprehension. The earlier solutions are more naive and not recommended as [best practices](https://peps.python.org/pep-0020/), but they are still good exercises to solidify the fundamentals. Between the more Pythonic list comprehension and functional style, it will require an experienced programmer to weigh the pros and cons to balance readability, efficiency, and maintainability. Lastly, using the step argument from range might seem obvious, but it requires deeper knowledge of Python and may confuse peer programmers trying to maintain the code. Therefore, it is essential for a programmer to share his or her knowledge with others, so that the team can grow as a whole, thereby increasing the efficiency (or Pythonicity?) of a software project.

Can you find other creative ways to solve this challenge?

---

# Further resources:
1. [Python For Loop With Index](https://pythonguides.com/python-for-loop-index/)
2. [When to Use a List Comprehension in Python](https://realpython.com/list-comprehension-python/)
3. [The Python Tutorial](https://docs.python.org/3/tutorial/index.html)
4. [Codewars](https://codewars.com)
5. [LeetCode](https://leetcode.com)