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 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.
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.)
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
.
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.
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
.
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β. It provides a concise way to create a list from an iterator. This is when the solution starts to be more βPythonicβ.
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
.
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.
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.
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, 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?
Top comments (0)