12 Python tips for concise and readable code

12 Python tips for concise and readable code

Here are 12 quick tips that I hope will help you write cleaner, more concise, and more readable code!

Dictionary comprehension

Dictionary comprehension is list comprehension's forgotten brother. Although similar in syntax, list comprehension seems to be talked about way more. So let's look at dictionary comprehension.

Lets say you want to reverse a dictionary by making the keys the values and vise-versa.

x = {"1": 1, "2": 2}
{v: k for k, v in x.items()}
# {1: "1": 2: "2"}

It's as easy as that, for a more generic understanding it works as so,

{[use of variables] for [key and value] in [dictionary items]}

zip operator

The zip operator is super useful when working with a bunch of lists and data needs to be joined together. Let's say you have a list of people's names and a list of their ages. We could use dictionary comprehension with an enumerator but that could get sloppy. Here's the syntax using zip.

names = ["Jake", "Marco", "Jose"]
ages = [32, 12, 21]

dict(zip(names, ages))
# {"Jake": 32, "Marco": 12, "Jose": 21}

list(zip(names, ages))
# [("Jake", 32), ("Marco", 12), "Jose": 21}]

Now that's so easy! It can be used to output a dict, list of tuples, or even kept as a zip object (but that's rarely used).

int(bool)

Although this one may be more rare, there are times when you need to use a boolean in a mathematical function. If that's you, then you're in luck! With a simple command you can turn booleans into integers.

int(True)
# 1

int (False) 
# 0

While simple at it's base, it could be powerful when you want to know whether you should scale a number based off of logic.

bank_account = 1000

# With int(bool)
lemonade_price = 0.25
bought_lemonade = True
bank_account = bank_account - (int(bought_lemonade) * lemonade_price)

# Without int(bool)
if bought_lemonade:
  bank_account = bank_account - lemonade_price

Ternary operations

Ternary operations are essentially an if-else statement in one-line.

# With ternary
prize = 10000 if position = "first" else 0

# Without ternary
if position = "first":
  prize = 10000
else:
  prize = 0

:= operator

:= is known as the walrus operator (new in Python 3.8) allows you to create a variable within any flow-control statement (if, else, while, etc.). This allows you to avoid having to set a variable either before or after the logical statement to then use it in another operation.

# With walrus
lst = [1, 2, 3, 4, 5]
if (length := len(lst)) > 10:
    print(f"List is too long ({length} elements, expected <= 10)")

# Without walrus
lst = [1, 2, 3, 4, 5]
length = len(lst)
if length > 10:
    print(f"List is too long ({length} elements, expected <= 10)")

Here we see, length is used in the logical operation while also being used later on in the print statement.

if statements without else

Although not really an operator, it's more of a way to design functions that allows for less indention and for what I think is more readable code. It allows you to create a logical flow without the use of elif or else statements by utilizing return.

def letter_grade(grade):
  if grade > 93:
    return "A"

  if grade > 90:
    return "A-"

  ...

  return "F"

letter_grade(91)
# "A-"

As we can see here, because return exists the function, we're able to have a set of ifs w/o the need for an elif or else. If none of the grade checks are true, it'll fall to the end where the "F" would be returned. We can think of this as being the equivalent as the first "if" being a true "if", the later ones being "elif"s and the final return statement being the "else".

collections.defaultdict

This is the first tip that requires an import but don't worry it's built-in with Python. defaultdict allows you to create a dictionary with some base values so you don't have to use special cases for when you're first inserting a key. If this sounds confusing, lets see if the example helps.

from collections import defaultdict

fruits = ["apple", "banana", "orange", "apple"]

# Without defaultdict
fruit_count = {}
for fruit in fruits:
  if fruit in fruit_count:
    fruit_count[fruit] = 1
  else:
    fruit_count[fruit] += 1

# With defaultdict
fruit_count = defaultdict(int)
for fruit in fruits:
  fruit_count[fruit] += 1

Notice how there's no if statement? defaultdict knows that it's going to be storing integers, so when a new key is created, the value becomes 0. So on the first operation, it knows to take 0, increment it by 1, and storing it as the value.

functools.partial

Another import! But also built-in to Python so not to worry. This function allows you to create a function from a function + arguments. It's useful if you're going to be calling the same function and it using some of the same parameter values and some that are different.

from functools import partial

def exponent(base, power):
  return base**power

raised_to_the_second = partial(exponent, power = 2)

raised_to_the_second(2)
# 4

exponent(2, 2)
# 4

As we see here, raised_to_the_second is just exponent with power having a default argument (and therefore optional) compared to exponent where it must be supplied each time.

Underscores in numbers

I'm almost a little embarrassed to say I've been coding in Python 3.6 for a while (probably ~ 3 years) and just heard about this last week! I've always had the issue in programming (or honestly whenever storing numbers digitally) with having to count the 0s in a big number to see if it was thousands, millions, etc. Lucky Python 3.6 has a little trick up it's sleeve!

100_000
# 100000

10_000
#10000

1_0_0
# 100

It's so much easier to read! Well maybe not the 1_0_0 but wanted to show it doesn't need to be used only in 3s.

f-strings

F-strings are just amazing. Shoutout Python 3.6 with some really great improvements. Although they're not the first solution to string formatting in Python, I think it's so much better than using + or .format. So let's check them out.

# f-string
x = 1
y = 2
f"Your numbers are {x} and {y}."
# Your numbers are 1 and 2.

# .format
"Your numbers are {} and {}".format(x, y)
# Your numbers are 1 and 2.

# +
"Your number are " + str(x) + " and " + str(y) + "."
# Your numbers are 1 and 2.

Although people may have different opinions, I really do think it's night and day when ready f-strings compared to all the other solutions I've found.

*args & **kwargs

This is a topic I feel most beginner and even intermediate Python developers stay away from because they're quite scary at first but once you get to know them, they can be a great advantage to you. Especially when creating wrappers or making extensible software. As they're similar but they're own concept lets break it down into two separate parts.

Args

args contains all the positional arguments to a function when written as such *args. Note that it can be named anything, but *args is typically reserved as to single the generic use of the term. Now, lets look at an example.

def print_everything(*args):
  for arg in args:
    print(arg)

print_everything("apple", "orange", "kiwi")
# apple
# orange
# kiwi

As simple as that, args just takes all the positional arguments into a list that can then be iterated over.

Kwargs

kwargs is like the more complex cousin of args. While args deals with positional arguments, kwargs deals with key-word arguments (i.e. key-word arguments)

def print_everything_with_names(**kwargs):
  for key, value in kwargs.items():
    print(f"{key}: {value}")

print_everything_with_names(name="Anthony", age="21")
# name: Anthony
# age: 21

Those are args and **kwargs! If you want to learn any more about the underlying mechanics of the `and**` read here. If you don't want to read but want to know the terminology, it's called "packing/unpacking".

dataclasses

Dataclasses are a type of class when primary purpose is to store and return data. Instead of having to worry about things like __init__ and __repr__, you just set the fields you want to store and dataclass does it all.

from dataclasses import dataclass

@dataclass
class Student:
  name: str
  age: int
  gpa: float

student = Student(name="Mark", age="23", gpa=3.0)
student.name
# Mark

While dataclass is a simple to use class, when it comes to more complex needs Pydantic has become the go-to (for me at least).

Summary

Thanks for reading! I hope this helps and would love to hear any other cool tips that people have in the comments!