DEV Community

Bas codes
Bas codes

Posted on • Originally published at bas.codes

Writing Idiomatic Python Code

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:
    ...
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:
  ...
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

f-strings

f-strings provide a template-like mini-language inside Python. You can, for example, align text, or specify precisions of floats.

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}")

Enter fullscreen mode Exit fullscreen mode

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}

Enter fullscreen mode Exit fullscreen mode

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'}:
    ...
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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)")
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

Pattern Matching

Pattern Matching is a very handy feature added in Python 3.10.

I had a Twitter thread on this in March:

What are some things you consider as idiomatic Python?

Share your ideas! ๐Ÿ’ก๐Ÿ‘‹

Discussion (8)

Collapse
vulcanwm profile image
Medea

Wow I didnโ€™t know half of these existed!

Collapse
bascodes profile image
Bas codes Author

Glad that it helped!

Collapse
rouilj profile image
John P. Rouillard

Are asserts still removed when python runs with -O? If so what does that mean for code accuracy by using asserts?

Collapse
bascodes profile image
Bas codes Author

asserts would be removed. Apart from tools like py2exe I have barely seen -O in the wild.
But, yes, the asserts would render useless โ€“ so it's important to have a good test coverage.

Collapse
ricardochan profile image
Ricardo Chan

Really useful tips :)

Collapse
bascodes profile image
Bas codes Author

Thank you!

Collapse
alexmacniven profile image
Alex Macniven

I really like these simple, easy to understand examples ๐Ÿ‘

Collapse
kilo59 profile image
Gabriel

Context managers, generators, operator overloading.