Unlock the Full Potential of Python

Tips and Tricks for Readable and Efficient Code

·

11 min read

Python is one of the most popular programming languages among developers and Data Scientists. Developers use the language for backend and frontend development. Data Scientists and Analysts use Python to analyze data. The main machine-learning libraries are available in Python. For this reason, Python is very popular among Data Scientists.

In this article, we show some Python tips and tricks. The tips and tricks will help you to make your Python code more efficient and readable. You can use these tips directly in your next Python project. We structured the tips in such a way that you can read all the tips independently of each other. Read the headline and decide which tip or trick interests you!

Let’s start right now!


🎓 Our Online Courses and recommendations

Our Online Courses and recommendations


Use enumerate()

In Python, you usually write a for() loop over an iterable object. So you don’t need a count variable to access elements. However, sometimes it is necessary to have a counter variable. There are several ways to implement such a for() loop. We will show you two variants today. The following example code shows variant A.

# variant A

company_list = ["Tesla", "Apple", "Block", "Palantir"]

i = 0
for company in company_list:
    print(i, company)
    i+=1

# Output:
# 0 Tesla
# 1 Apple
# 2 Block
# 3 Palantir

In this example, we create a counter variable i. We increment this counter variable at each iteration. This implementation is error-prone. You have to remember to update i at each iteration.

You can avoid this error by using enumerate() in the loop. Instead of inserting the iterable directly into the for() loop, you put the iterable into the brackets of enumerate(). The following example shows how it works:

# variant B

company_list = ["Tesla", "Apple", "Block", "Palantir"]

for count, company in enumerate(company_list, start=0):
    print(count, company)

# Output
# 0 Tesla
# 1 Apple
# 2 Block
# 3 Palantir

The enumerate() function also gives you the option to set the start value of the counter. The function improves your code in terms of maintainability, readability and efficiency.

Use zip()

Python’s zip() function creates an iterator that combines elements from two or more iterables. With the resulting iterator, you can solve common programming problems. We show you how to use the Python function zip() with practical examples.

You can think of the zip() function as a physical zip. The analogy will help you understand the zip() function. The zip() function has iterables as arguments and returns an iterator. This iterator creates a series of tuples. The tuples contain the elements from each iterable. The zip() function accepts any type of iterable (e.g., files, lists, tuples, dictionaries, etc.). The following code block shows how it works with two lists as arguments.

list_a = [0,1,1]
list_b = [2,3,5]

zipped = zip(list_a, list_b)
list(zipped)

# Output
# [(0, 2), (1, 3), (1, 5)]

Note that the zip() function returns an iterator. We need to use list() to call the list object. In the above example, the length of the iterables is the same. However, it is also possible that the iterables do not have the same length. We will now take a closer look at this case.

list(zip(range(7), range(42)))

# Output
# [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]

We see in the code example above that the length of the shortest iterable is used. The zip() function ignores the remaining elements in longer iterables. Since Python 3.10, zip() has a new optional argument called strict. The key purpose of this argument is to provide a safe way to handle unequal-length iterables. Look at the code example below.

list(zip(range(7), range(42), strict=True))

# Output
# ---------------------------------------------------------------------------
# ValueError                                Traceback (most recent call last)
# Cell In[2], line 1
# ----> 1 list(zip(range(7), range(42), strict=True))
#
# ValueError: zip() argument 2 is longer than argument 1

We get a ValueError because the length does not match. The argument strict is useful when you need to make sure that the function only accepts iterables of equal length.

A loop over multiple iterables is one of the most common use cases for Python’s zip() function. The zip() function is useful for iterating over sequences (e.g. list, dictionary). We show you how to use zip() to run multiple iterables at the same time. We also compare the classical procedure with the zip() method. The following code example shows the classic way to iterate through two lists.

from time import perf_counter

list_a = range(0, 10_000, 1)
list_b = range(10_000, 20_000, 1)

start = perf_counter()
for i in range(len(list_a)):
    a = list_a[i]
    b = list_b[i]
print(perf_counter()-start)

# Output
# 0.00775 s -> 7.75 ms

In this example, we iterate through the two lists list_a and list_b. In the for() loop, we must access individual list elements manually. This process increases the runtime of the code. Now let’s look at how we can improve the code with the zip() function.

from time import perf_counter

list_a = range(0, 10_000, 1)
list_b = range(10_000, 20_000, 1)

start = perf_counter()
for a,b in zip(list_a, list_b):
    # do something
    pass

print(perf_counter()-start)
# Output
# 0.00093 s -> 0.93 ms

In this example, we again iterate over the lists list_a and list_b. Now we can access the individual elements of the lists directly in the for() loop. The advantages are shorter code and improved runtime.

Don’t be surprised at the _ in the numbers. The solution is in the last tip.

Unpacking and Packing function arguments

We look at an add function that receives two arguments. In the following, you see the classic implementation as you know it from other programming languages.

def add_function(a, b):
    print(a + b)

foo = 2
bar = 5
add_function(foo, bar)

# Output
# 7

In this example, we add 5 and 2. The result is 7. In Python, we can also pass a list of arguments to the function. This is really cool!

# Unpacking
def add_function(a, b):
    print(a + b)

foo1 = [2, 5]
foo2 = {'a':2, 'b':5}
add_function(*foo1)
add_function(**foo2)

# Output
# 7
# 7

In this example, we use * to unpack the list so that all elements of it can be passed. In addition, we use ** to unpack the dictionary.

We can also pass any number of arguments. That is called packing.

# Packing
def add_function(*args):
    print(sum(args))

add_function(1, 1, 2, 3, 5)

# Output
# 12

The add_function() above packs all the arguments into a single variable. With the packed variable we can do everything as with a normal tuple. args[0] and args[1] give you the first and second argument.

Unpacking and Packing make your code less error-prone and cleaner. It is so simple that you can use it directly in your next project.


🎓 Learn Python and Machine Learning

Do you want to take your Python and ML skills to the next level? Discover The Complete Python, Machine Learning, AI Mega Bundle*.

Learn Python and Machine Learning*

It is a completely self-paced online learning course.

Here you’ll discover:

  • Everything from the ABC of Python syntax to using your own web applications

  • Machine Learning and AI: Providing hands-on experience with real-world projects

  • Ready-to-use Project Templates and Source Code

👉🏽 Enroll today and take the next step in mastering Python and Data Science.*


Inplace value exchange

Many developers use a temporary variable for caching. There is an easier and faster way without creating an additional variable. Take a look at the following code.

# with additional tmp variable
a = 2
b = 4
tmp = a
a = b
b = tmp
print(a, b)

# Output
# 4 2

# without additional tmp variable
a = 2
b = 4
a, b = b, a
print(a, b)

# Output
# 4 2

You see it is very simple. However, this approach is not limited to variables. You can also apply it to lists. The following code shows an example with the first Fibonacci numbers.

example_list = [1, 1, 2, 3, 5]
example_list[0], example_list[3] = example_list[3], example_list[0]

print(example_list)

# Output
# [3, 1, 2, 1, 5]

In this example, we rearrange the order of the elements of the list. Often it’s the simple things that make your code cleaner. Remember this tip for your next Python project.

Avoid exceptions with Dictionary Get() method

As a Python developer, you have seen the KeyError exception many times. The goal is to prevent unexpected KeyError exceptions from occurring. The following code shows the exception with a small example.

dict={1: 2, 3: 4, 3: 5}
print(dict[8]) 

# Output
# ---------------------------------------------------------------------------
# KeyError                                  Traceback (most recent call last)
# Cell In[30], line 2
#      1 dict={1: 2, 3: 4, 3: 5}
# ----> 2 print(dict[8]) 
#
# KeyError: 8

A solution to stop this error is to use .get() function. Let’s use .get().

dict={1: 2, 3: 4, 3: 5}
print(dict.get(8)) 

# Output
# None

Now, we don’t get a KeyError exception because we use the safer .get() method. If the key is not found, the return value is None (default). You can also specify the default value yourself by passing a second argument.

dict={1: 2, 3: 4, 3: 5}
print(dict.get(8), 0) 

# Output
# 0

In this example, we change the default return value to 0. The .get() function makes your code safer.

Use context managers

Python context managers are a powerful feature of the language for managing resources, networks and database connections. Resources such as files, network or database connections are automatically set up and taken down when needed or not needed. Context managers are useful in projects where resources are scarce or when dealing with multiple connections. In addition, context managers can help with error handling. In Python, the with statement implements a context manager. Let’s look at an example.

import json
with open('config/config_timescaleDB.json') as timescaleDB_file:
    config_timescaleDB_dict = json.load(timescaleDB_file)

First, we look at the syntax. After the with statement stands the context manager expression. This expression is responsible for the setup of the context manager object. Examples of a context manager expression are connections to databases or opening a file (as in our example). After the as is an optional variable that receives the result of the __enter__() method of the context manager. In our example, this is the JSON file. Then we come to the body. In the body, we execute the code within the context. In our example, we load the JSON file and save the content in a dictionary.

After the code in the body has been executed, the with statement calls the __exit__() method of the context manager. The __exit__() method performs the cleanup (for example: closing the file or freeing resources). In practice, the withstatement is essential, especially when working with database connections. The use of context managers ensures that database connections are closed automatically. Efficient resource management becomes possible.

When using context manager, a lot of manual work is taken away from the developer by doing a lot automatically. That leads to the fact that not all steps are visible in the code. So it is hard to understand the code when you look at it for the first time.

Context managers should be used for memory optimization and clean resource management because they take a lot of work off your hands.

Working with large numbers

Have you ever worked with large numbers in Python? Which number is more readable 1000000 or 1,000,000? The second one, right? Unfortunately, the second notation does not work in Python. In Python, however, you can use a _instead of a ,. Let’s look at an example.

# bad way
million = 1000000     # 1 million
thousand = 1000        # 1 thousand
total = million + thousand
print(f'{total:,}')  

# Output
# 1,001,000

# smart way
million = 1_000_000     # 1 million
thousand = 1_000        # 1 thousand
total = million + thousand
print(f'{total:,}')

# Output
# 1,001,000

Both methods give the same result. The only difference is the representation of large numbers. The second method is easier to read and avoids errors.

Conclusion

In this article, we have learned how to write cleaner and safer code. The key findings are:

  • Use the enumerate() function in a loop: You get a count of the current iteration and the value of the item at the current iteration.

  • Use zip() in a loop to iterate over multiple iterables.

  • Use Unpacking and Packing for less error-prone and cleaner code.

  • Avoid temporary variables with in-place value exchange.

  • Avoid the KeyError exception with the dictionary .get() method.

  • Use context managers for your resource management.

  • Use _ for large numbers to make your code readable.


👉🏽 Join our free weekly Magic AI newsletter for the latest AI updates!

👉🏽 Elevate your Python and ML skills with our top course recommendations!

Did you enjoy our content and find it helpful? If so, be sure to check out our premium offer! Don't forget to follow us on X. 🙏🏽🙏🏽

Thanks so much for reading. Have a great day!


Useful resources

* Disclosure: The links are affiliate links, which means we will receive a commission if you purchase through these links. There are no additional costs for you.