The Beauty of PEP 515: Underscores in Numeric Literals

Thom Hopmans · June 20, 2024

The Beauty of PEP 515: Underscores in Numeric Literals

The introduction of new features in the Python landscape often brings a wave of excitement and controversy. Among the many Python Enhancement Proposals (PEPs) that have shaped the language, PEP 515 stands out for its simplicity, because it allows the use of underscores in numeric literals. This might seem like a minor syntactic sugar at first glance. However, its benefits in enhancing code readability and maintainability are undervalued in my humble opinion.

What is PEP 515?

PEP 515 “Underscores in Numeric Literals” was accepted in Python 3.6 (released on 22 Dec 2016) and provides a way to include underscores in numeric literals for the purpose of improving readability. This means that you can write large numbers in your code with underscores as separators, much like commas or spaces in written numbers.

# Before PEP 515
large_number = 1000000000

# After PEP 515
large_number = 1_000_000_000

Both lines of code represent the same numeric value, but the latter is much easier to read at a glance, especially in contexts where such large numbers are common.

PEP 515 allows you to place underscores where they make the most sense logically. For example, in a credit card number, you might group digits in sets of four.

credit_card_number = 1234_5678_9012_3456

The underscores serve as a natural grouping mechanism that mirrors how we would write numbers in everyday language. This not only makes the code cleaner but also reduces cognitive load, making it easier to spot errors and understand the code quickly.

Underutilized and lesser-known

Despite its introduction in Python 3.6 more than 7 years ago, the feature provided by PEP 515 remains surprisingly underutilized and lesser-known among many developers. Often overshadowed by more headline-grabbing additions (shoutout to the Walrus Operator in PEP 572), the ability to use underscores in numeric literals quietly improves how we write and read numbers in our code.

For data analysts and scientists, working with large datasets often entails handling substantial numerical values, whether in financial data, population statistics, or scientific measurements. The subtle enhancement of adding underscores in numeric literals significantly aids in preventing errors when working with such values.

Imagine the potential impact a mistake of forgetting one trailing zero could have on your analysis or application. You wouldn’t be the first to make a mistake as given in the example below.

# Annual revenue in dollars, with one missing trailing zero 
annual_revenue = 1056700

# Annual revenue in dollars, without a missing trailing zero
annual_revenue = 10_567_000

Utilizing PEP 515 can transform code from a jumble of digits into a well-structured and easily interpretable script, potentially mitigating serious errors.

Ruby’s Precedent and Inspiration

Python is not the only language that has recognized the value of readability through numeric separators. Amongst others, Ruby has long supported underscores in numeric literals.

# Ruby's numeric literals with underscores
large_number = 1_000_000_000

PEP 515 specifies a list of other languages in Prior Art that also implement placement of underscores, although the exact implementation varies per language.

Conclusion

PEP 515 may seem like a small change in Python’s history, but its impact on code readability and maintainability is significant. By allowing underscores in numeric literals, it embraces a more human-readable approach to handling numbers, reducing errors, and making code more accessible. For data practitioners and programmers, PEP 515 offers a clear and concise way to represent large and complex numbers, making scripts easier to write, read, and maintain.

So, the next time you write a large number in your Python code, embrace the beauty of underscores, and let your numbers tell their story with clarity and grace.

💯 _ 💯 _ 💯