Basic Tips - Intermediate Tips - Advanced Tips
- Use
for index, item in enumerate(list):
to loop over theindex
of an item and theitem
at the same time. - Use
for key, value in dictn.items():
instead of iterating overkeys
and then getting values asdictn[key]
. - Use
for item1, item2 in zip(list1, list2):
to iterate over two lists at a time.
Instead of calling close()
manually when you finish accessing a file, consider using a with
statement. It's shorter, more readable, and less error-prone, as it will take care of closing the file automatically for you.
Instead of doing this... | try this: |
---|---|
file = open(file_path, 'w')
file.write('Hello, World!')
file.close() |
with open(file_path, 'w') as file:
file.write('Hello, World!')
# The file is closed automatically. |
An f-string
is preceded by the letter f
and allows for inserting variables between curly braces {}
.
Using f-strings
is generally faster and makes your code more readable.
Instead of doing this... | try this: |
---|---|
name = input('Enter your name: ')
surname = input('Enter your surname: ')
print('Hello, ' + name + ' ' + surname) |
name = input('Enter your name: ')
surname = input('Enter your surname: ')
print(f'Hello, {name} {surname}') |
If you want to separate a string into a list of substrings, use lst = text.split(separator)
.
Use text = separator.join(lst)
to merge a list of strings together.
Instead of doing this... | try this: |
---|---|
names = ['Olivia', 'Nicholas', 'Violet']
text = ''
for name in names:
text += name + ', '
text = text[:-2]
print(text)
# >> 'Olivia, Nicholas, Violet' |
names = ['Olivia', 'Nicholas', 'Violet']
text = ', '.join(names)
print(text)
# >> 'Olivia, Nicholas, Violet' |
Instead of iterating over a list
to remove duplicate elements, take advantage of the properties of certain data structures, like the set
, which can only contain distinct elements.
Transform your list
into a set
to remove duplicate elements. You can transform it back to a list
afterwards if you need to.
Instead of doing this... | try this: |
---|---|
cities = ['London', 'Paris', 'London']
unique_cities = []
for city in cities:
if city not in unique_cities:
unique_cities.append(city)
print(cities)
# >> ['London', 'Paris'] |
cities = ['London', 'Paris', 'London']
unique_cities = set(cities)
print(cities)
# >> {'London', 'Paris'}
cities = list(cities)
print(cities)
# >> ['London', 'Paris'] |
Instead of repeating a variable in different conditions of an if
statement, check for belonging against a set of allowed values. This is shorter, less prone to error, and makes it easier to add or remove allowed values in the future.
Instead of doing this... | try this: |
---|---|
if variable == a or variable == b:
res = do_something(variable) |
if variable in {a, b}:
res = do_something(variable) |
It is possible to write inline if
statements to make assignments to variables based on a condition.
Whether to use one or the other is mostly subjective and depends on which one you find more aesthetic and easier to read.
Regardless, you should know and understand both in case you come across them.
This code... | is equivalent to this code: |
---|---|
if b > 10:
a = 0
else:
a = 5 |
a = 0 if b > 10 else 5
|
Learn that you can use or
to set a fallback value to assign in case another variable is None
or False
.
Instead of doing this... | try this: |
---|---|
if data:
lst = data
else:
lst = [0, 0, 0] |
lst = data or [0, 0, 0]
|
In Python, it's much more common to try:
to do something and catch all possible exceptions, than to control input and output values using if
statements.
More likely than not, the function already does the necessary value checks from the inside and raises all possible errors.
The only thing left for you to do is to catch them and define what should happen in case of error inside an except Exception:
clause.
This is why, instead of checking if a file
exists and if you have permissions before opening it, you should just try to open it.
Instead of doing this... | try this: |
---|---|
if not os.path.isfile(filename):
print('File does not exist.')
if not os.access(filename, os.W_OK):
print('You dont have write permission.')
with open(filename, 'w') as file:
file.write('Hello, World!')
|
try:
with open(filename, 'w') as file:
file.write('Hello, World!')
except FileNotFoundError:
print('File does not exist.')
except PermissionError:
print('You dont have write permission.')
except OSError as exc:
print(f'An OSError has occurred:\n{exc}') |
Learn how to use text.ljust(length)
to align text to the left, filling it with spaces until it reaches a desired length
.
Use text.rjust(length)
or text.center(length)
to align text to the right or center respectively.
Set length = os.get_terminal_size().columns
to center text relative to your terminal's width.
Instead of doing this... | try this: |
---|---|
text = "Hello, World"
length = 20
text = " "*((length//2) - (len(text)//2)) \
+ text \
+ " "*((length//2) - (len(text)//2))
print(text)
# >> " Hello, World " |
text = "Hello, World"
length = 20
text = text.center(length)
print(text)
# >> " Hello, World " |
It's usually better to use comprehensions over for loops in Python when possible, as they are usually faster and shorter. However, it can be difficult to transform long and complex for loops into comprehensions.
If you are iterating over items
, processing them in some way, and appending the results to a list to return it afterwards, try doing this instead:
First, extract the body of the loop to a separate function that processes one item
at a time. Let's call it process_item()
.
Then, build your list comprehension by calling process_item()
over all the items
.
Instead of doing this... | try this: |
---|---|
items = [item1, item2, item3, item4...]
new_items = []
for item in items:
item = do_something(item) # Extract
item = do_smth_else(item) # this
... # logic.
new_items.append(item)
|
items = [item1, item2, item3, item4...]
def process_item(item): # 'process_item'
item = do_something(item) # now does the
item = do_smth_else(item) # processing
... # for a single
return item # item.
new_items = [process_item(item)
for item in items] |
List, set, dictionary, and generator comprehensions may generally be shorter and run faster, but they can also be much harder to read. This is why, instead of writing your comprehensions in a single line, try splitting them into multiple lines.
Notice how each keyword (for
, in
, if
...) starts a new line. This makes the comprehension more comfortable to both read and modify if you need to add new conditions in the future.
Instead of doing this... |
---|
comments = {line_idx: line.strip() for line_idx, line in enumerate(file) if line.startswith('#')} |
try this: |
---|
comments = {line_idx: line.strip()
for line_idx, line
in enumerate(file)
if line.startswith('#')} |
Iterating is expensive in Python and most common operations that require iteration can be done through functions that are either built-in or available in popular libraries/modules like functools
, itertools
, and numpy
. The underlying code for these functions is usually written in C and they are highly optimized. Use them whenever possible and iterate only when strictly necessary.
Instead of doing this... | try this: |
---|---|
numbers = [1, 3, 4, 5, 7, 9, 2]
sum = 0
for number in numbers:
sum += number
avg = sum / len(numbers) |
from numpy import mean
numbers = [1, 3, 4, 5, 7, 9, 2]
avg = mean(numbers)
|
Your processor has multiple cores that can execute several tasks at the same time, but by default your code usually runs on a single core.
However, Python provides an easy way to make use of multiple processes to achieve parallelism and speedups that can go from x4 to x16 and even more for some machines.
Instead of doing this... | try this: |
---|---|
requests = [req1, req2...]
results = []
for request in requests:
res = process_request(request)
results.append(res)
# Runs in 1m 22s, depending on HW. |
from multiprocessing import Pool
requests = [req1, req2...]
with Pool as p:
results = p.map(process_request, requests)
# Runs in 16s, depending on HW. |
It's a common task to get the first element of a list that has some specific property. The next()
function can help you do that.
Instead of doing this... | try this: |
---|---|
numbers = [1, 3, 4, 5, 7, 9, 2]
result = None
for number in numbers:
if number > 3:
result = number
break
print(result)
# >> 4 |
numbers = [1, 3, 4, 5, 7, 9, 2]
try:
result = next(number for number in numbers
if number > 3)
except StopIteration:
result = None
print(result)
# >> 4 |
Here are some secret extra tips, for making it all the way to the end.
- Use multiple assignments and commas to give values to more than one variable at a time:
a, b, c = 1, 2, 3
- Or easily swap two variables in a single line:
a, b = b, a
all(a, b, c)
returnsTrue
if a, b and c areTrue
.any(a, b, c)
returnsTrue
if at least one of a, b or c isTrue
.
You can use them as conditions in your if
statements.
Instead of writing separate conditions (if min < value and value < max:
):
- Use intervals:
if min < value < max:
- Or use ranges:
if value in range(min, max):
Keep in mind that min
is included in the range()
, but max
is not.