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(unique_cities)
>> ['London', 'Paris'] |
cities = ['London', 'Paris', 'London']
unique_cities = list(set(cities))
print(unique_cities)
>> ['London', 'Paris'] |
Instead of repeating a variable in different conditions of an if
statement, check for belonging against a tuple of allowed values. This is shorter, less prone to error, and makes it easier to add or remove allowed values in the future.
You can also use if min <= variable <= max
or if variable in range(min, max + 1)
to check if a variable is within a range.
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) |
You probably already know you can unpack tuples like this first = tuple[0]
and second = tuple[1]
.
But did you know you can also do first, second = tuple
?
If the tuple has more than two elements, you can use first, second, *rest = tuple
to unpack the first two elements and assign the rest to a list called rest
.
Instead of doing this... | try this: |
---|---|
runners = 'John Mike Greg Luke Bob'
positions = runners.split(' ')
first = positions[0]
second = positions[1]
rest = positions[2:]
|
runners = 'John Mike Greg Luke Bob'
first, second, *rest = runners.split(' ')
print(first)
>> 'John'
print(second)
>> 'Mike'
print(rest)
>> ['Greg', 'Luke', 'Bob'] |
You can encase a statement in parentheses ()
and split it into multiple lines.
This is useful for long statements that are difficult to read.
Instead of doing this... | try this: |
---|---|
...
return Popen(...).stdout.read().decode()...
|
...
return (Popen(cmd, shell=True, stdout=PIPE)
.stdout
.read()
.decode()
.strip()) |
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]
total = 0
for number in numbers:
total += number
avg = total / len(numbers) |
numbers = [1, 3, 4, 5, 7, 9, 2]
avg = sum(numbers) / len(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]
result = next((number
for number in numbers
if number > 3),
None) # Fallback value.
print(result)
>> 4 |
very good written and clean layout, thank you for sharing!