Free Python reference · JavaScript timeline

Python 3.9: Convenient collections and exact text removal

Merge dictionaries with explicit precedence, annotate built-in collections, and remove a whole prefix or suffix without strip confusion.

Released · Editorial review October 25, 2026

Introduced in Python 3.9 · Language behavior

Dictionary union with |

left | right builds a new dictionary containing both mappings' entries. When a key exists in both, the right-hand value wins. This makes configuration layers readable: defaults on the left, overrides on the right.

The originals are preserved by |. The related |= updates the left dictionary in place. Neither operation recursively merges nested dictionaries; a conflicting nested value is replaced as a whole.

The example merges scalar options and prints the original default as evidence that the non-mutating form did not change it.

Earlier approach

settings = dict(defaults)
settings.update(overrides)

Context fragment; read it with the example below.

Worked example

Minimum Python 3.9

defaults = {"theme": "light", "size": 12}
overrides = {"size": 16}
settings = defaults | overrides
print(settings)
print(defaults["size"])

Expected output

{'theme': 'light', 'size': 16}
12

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

Common mistake

Reversing the operands changes which conflicting value wins. A shallow merge does not combine nested configuration fields.

Try it yourself

Add a new key to overrides, then compare defaults | overrides with overrides | defaults.

Read more: Python 3.9 release notes · PEP 584

Introduced in Python 3.9 · Typing

Type hints with list[str] and dict[str, int]

Built-in collections can be parameterized directly in type expressions. Write list[str] rather than importing List solely to describe a list of strings. This works naturally for nested structures such as dict[str, list[int]].

The annotation documents element types for tools and readers. It does not cause Python to inspect every element at runtime. Function behavior is still determined by its implementation.

Use isinstance(value, list) for a basic runtime container check. A parameterized type such as list[str] is not an element-validation rule that you can pass to isinstance.

Earlier approach

from typing import List

def total(scores: List[int]) -> int:
    return sum(scores)

Context fragment; read it with the example below.

Worked example

Minimum Python 3.9

def total(scores: list[int]) -> int:
    return sum(scores)

print(total([2, 4, 6]))
try:
    isinstance(["Ada"], list[str])
except TypeError:
    print("parameterized types are not runtime validators")

Expected output

12
parameterized types are not runtime validators

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

Common mistake

A list annotation does not make the list homogeneous. Validate untrusted elements separately and let static analysis check ordinary code.

Try it yourself

Annotate a mapping from student names to lists of scores. Describe which checks are needed when that mapping comes from JSON.

Read more: Python 3.9 release notes · PEP 585

Introduced in Python 3.9 · Standard library

Remove a prefix or suffix exactly

removeprefix() and removesuffix() remove one whole matching affix. If it is absent, the string's contents stay unchanged. They are useful for filenames, identifiers, and labels with a known literal wrapper.

The older strip, lstrip, and rstrip methods take sets of characters rather than a whole substring. For example, removing the character set .txt from an end is different from removing exactly the suffix .txt.

Removal is case-sensitive and applies once. These operations return strings and do not modify the original. An empty affix leaves the contents unchanged.

Worked example

Minimum Python 3.9

name = "draft_report.txt"
print(name.removeprefix("draft_"))
print(name.removesuffix(".txt"))
print("draft_draft_report".removeprefix("draft_"))
print("Report.TXT".removesuffix(".txt"))

Expected output

report.txt
draft_report
draft_report
Report.TXT

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

Common mistake

Do not replace exact-suffix removal with rstrip(".txt"); that can remove unrelated trailing characters.

Try it yourself

Apply both methods to draft_report.txt to produce report. What should happen when the prefix is absent?

Read more: Python 3.9 release notes · PEP 616

Recap

Merge dictionaries with explicit precedence, annotate built-in collections, and remove a whole prefix or suffix without strip confusion. Prefer the clearest form that works on your project's supported Python versions.

Read the full Python 3.9 release notes →