What's New in Python 3.7: Data Classes and More

A guide to the major new features in Python 3.7. We take a deep dive into the new @dataclass decorator, which dramatically reduces boilerplate, and explore other improvements like built-in breakpoint() and asyncio enhancements.

Python 3.7, released in mid-2018, brought a host of new features and optimizations to the language. While there were many improvements, one feature stands out as a true game-changer for everyday Python programming: data classes.

Let's explore this major new feature and some of the other significant additions in Python 3.7.

The Star of the Show: Data Classes

If you've ever written a simple class just to hold some data, you know how much boilerplate code is involved. You have to write an __init__ method to assign the attributes, a __repr__ method for a clean string representation, and methods like __eq__ for comparison.

The new @dataclass decorator, available in the dataclasses module, automates all of this for you.

The Old Way:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Point(x={self.x}, y={self.y})"

    def __eq__(self, other):
        if not isinstance(other, Point):
            return NotImplemented
        return self.x == other.x and self.y == other.y

The New Way with @dataclass:

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

That's it! With the @dataclass decorator, Python automatically generates the __init__, __repr__, __eq__, and other special methods for you based on the type-annotated class attributes.

p1 = Point(10, 20)
p2 = Point(10, 20)

print(p1)      # Output: Point(x=10, y=20)
print(p1 == p2)  # Output: True

Data classes dramatically reduce boilerplate and make your code cleaner and more readable. You can customize their behavior with parameters to the decorator, for example, to create immutable data classes by setting frozen=True.

A New Way to Debug: breakpoint()

Python 3.7 introduces a new built-in function, breakpoint(), which provides an easy and consistent way to drop into the Python debugger (pdb).

The Old Way:

import pdb; pdb.set_trace()

The New Way:

breakpoint()

This is simpler to type and more flexible, as its behavior can be configured with an environment variable to use other debuggers.

asyncio and Timing Improvements

  • asyncio.run(): The asyncio library gets a new high-level run() function that simplifies the process of running a top-level coroutine. It automatically manages the event loop for you.

  • High-Precision Timers: The time module now has new functions like time.time_ns() that provide nanosecond-resolution timers, which is useful for precise performance measurements.

Dictionaries are Now Officially Ordered

In Python 3.6, as an implementation detail of a new dict implementation, dictionaries became insertion-ordered. In Python 3.7, this behavior is now an official part of the language specification. You can rely on the fact that when you iterate over a dictionary, it will yield items in the same order they were inserted.

Conclusion

Python 3.7 was a fantastic release for developer productivity. Data classes alone have fundamentally improved how we write data-holding objects in Python, making code cleaner and less verbose. Combined with quality-of-life improvements like breakpoint() and the official guarantee of ordered dictionaries, Python 3.7 continued the language's evolution as a powerful and developer-friendly tool.