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 if
s 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!