Free Python reference · JavaScript timeline

Python 3.7: Less boilerplate and predictable order

Dataclasses describe records, dictionary insertion order becomes a language guarantee, and asyncio gains a simpler entry point.

Released · Editorial review October 25, 2026

Introduced in Python 3.7 · Standard library

Dataclasses for small records

A dataclass generates methods such as an initializer, representation, and equality comparison from annotated fields. It is useful when the main responsibility of a class is holding named values.

A default field value becomes an optional initializer argument. Fields without defaults must precede fields with defaults. Annotations describe the fields but do not automatically reject values of the wrong type.

The example uses the generated initializer and compares records by field values. For a mutable default such as a list, use field(default_factory=list) so each instance gets its own collection.

Earlier approach

class Ticket:
    def __init__(self, title, done=False):
        self.title = title
        self.done = done

Context fragment; read it with the example below.

Worked example

Minimum Python 3.7

from dataclasses import dataclass

@dataclass
class Ticket:
    title: str
    done: bool = False

first = Ticket("Read release notes")
print(first.title, first.done)
print(first == Ticket("Read release notes"))

Expected output

Read release notes False
True

Example checked with CPython 3.14.8, CPython 3.15.0, Pyodide Python 3.13.2.

Common mistake

A dataclass is mutable by default. Even frozen dataclasses do not recursively freeze objects stored in their fields.

Try it yourself

Add a priority field with a default of 1. Compare two records with different priorities and predict their equality result.

Read more: Python 3.7 release notes · PEP 557

Introduced in Python 3.7 · Language behavior

Dictionary insertion order is guaranteed

Python 3.7 makes dictionary insertion order part of the language specification. CPython 3.6 had already preserved order as an implementation detail. Iterating keys follows their insertion order rather than alphabetical order.

Replacing the value of an existing key keeps its position. Deleting and reinserting a key puts it at the end. This is useful for stable report columns and predictable traversal.

Order does not become part of dictionary equality: dictionaries containing the same key-value pairs compare equal even if those pairs were inserted in a different order.

Worked example

Minimum Python 3.7

scores = {"Ada": 2, "Mina": 4}
scores["Ada"] = 5
print(list(scores))
del scores["Ada"]
scores["Ada"] = 5
print(list(scores))
print(scores == {"Ada": 5, "Mina": 4})

Expected output

['Ada', 'Mina']
['Mina', 'Ada']
True

Example checked with CPython 3.14.8, CPython 3.15.0, Pyodide Python 3.13.2.

Common mistake

Insertion order is not sorted order. Use sorted explicitly when alphabetical or numeric ordering is part of the contract.

Try it yourself

Insert a third person, update Mina, and predict the key order before running the example.

Read more: Python 3.7 release notes

Introduced in Python 3.7 · Standard library

asyncio.run() owns the event loop

asyncio.run() gives a standalone program an entry point for asynchronous code. It creates an event loop, runs the coroutine, and closes the loop afterward. Define asynchronous work in async def and call the entry point from normal synchronous code.

A coroutine does not run merely because you called its function; it must be awaited or scheduled. The example awaits a short suspension, then returns a normal value to the caller.

Use this example as a saved script in native Python. A notebook or browser environment may already have an event loop; in that situation use its supported top-level await rather than nesting asyncio.run().

Worked example

Minimum Python 3.7 · Run locally

import asyncio

async def main():
    await asyncio.sleep(0)
    return "ready"

print(asyncio.run(main()))

Expected output

ready

Example checked with CPython 3.14.8, CPython 3.15.0.

Common mistake

Calling asyncio.run() inside an already running event loop raises RuntimeError. Async code also does not make blocking CPU work automatically parallel.

Try it yourself

Add a second await inside main. Explain which function owns the loop and which function describes the work.

Read more: Python 3.7 release notes

Recap

Dataclasses describe records, dictionary insertion order becomes a language guarantee, and asyncio gains a simpler entry point. Prefer the clearest form that works on your project's supported Python versions.

Read the full Python 3.7 release notes →