You need to understand Python well before you can write idiomatic, or pythonic code in it.
But what does that even mean?
Here are some examples
Falsy and Truthy
Almost all data types can be interpreted as bool-ish. An empty list? Fals-y. A 3-character string? Tru-thy
# Instead of writing something like this
a = [1, 2, 3]
if len(a) > 0:
...
# You could write:
a = [1, 2, 3]
if a:
...
Ternary operator
Python does have a ternary operator by leveraging one-line ππs:
# Instead of the lenghty version
a = True
value = 0
if a:
value = 1
# You could shorten it to:
a = True
value = 1 if a else 0
Chained Comparison Operators
Python syntax should be as simple as possible. Thatβs why you can use mathematics-like notations like this
π» < π‘ < π·πΆ
# Instead of this
if x < 10 and x > 5:
...
# you can write this
if 5 < x < 10:
...
Multiple assignment and destructuring assignment
You can assign different variables in one line of Python code
# Instead of writing
x = 'foo'
y = 'foo'
z = 'foo'
# or
a = [1, 2, 3]
x = a[0]
y = a[1]
z = a[2]
# you could simplify to:
x = y = z = 'foo'
# and
a = [1, 2, 3]
x, y, z = a
f-strings
f-strings provide a template-like mini-language inside Python. You can, for example, align text, or specify precisions of float
s.
username = "Bas"
monthly_price = 9.99
# Instead of transforming each element,
print(username.rjust(10), '|', "{:3.2f}".format(monthly_price))
# you can use a single f-string
print(f"{username : >10} | {monthly_price:3.2f}")
list comprehensions / dict comprehensions
list and dict comprehensions are maybe the most Pythonic feature. It can be very useful for modifying data structures.
usernames = ["alice", "bas", "carol"]
# Instead of a loop:
users_with_a = []
for username in usernames:
if username.startswith("a"):
users_with_a.append(username)
# Use a list comprehension
users_with_a = [username for username in usernames if username.startswith("a")]
# Same for dicts: Instead of a loop
users_dict = {}
for username in usernames:
users_dict[username] = get_user_id(username)
# you can use dict comprehensions
users_dict = {username: get_user_id(username) for username in usernames}
in
keyword
Python has the in
operator that works on collections, like lists.
You could use it to check if an element is in a list of choices
name = 'Alice'
found = False
if name == 'Alice' or name == 'Bas' or name == 'Carol':
...
name = 'Alice'
if city in {'Alice', 'Bas', 'Carol'}:
...
enumerate
Whenever you need to not only access each element by a list but also need a counter in your loop, you can use enumerate
a = ["A", "B", "C"]
# Instead of using an index variable
for i in range(len(a)):
print(i, a[i])
# you could iterate the list as usual and attach a counter
for counter, letter in enumerate(a):
print(counter, letter)
The Walrus Operator
With the walrus operator introduced in Python 3.8, you have an assignment expression. That means that you could assign a value to a variable and access that value in the same line.
n = len(a)
if n > 10:
print(f"List is too long ({n} elements, expected <= 10)")
if (n := len(a)) > 10:
print(f"List is too long ({n} elements, expected <= 10)")
assert
Assertions inside your code not only make it safer but also help with understanding your rationale behind a particular line.
# Instead of a comment
def activate_user(user):
# User has to be a `UserModel` object
user.active = True
user.save()
# you can use assert
def activate_user(user):
assert type(user, UserModel)
user.active = True
user.save()
Pattern Matching
Pattern Matching is a very handy feature added in Python 3.10.
I had a Twitter thread on this in March.