PEP 841 – Adding Frozen Syntax to Optimize Immutable Types | peps.python.org
Following system colour scheme
Selected dark colour scheme
Selected light colour scheme
PEP 841 – Adding Frozen Syntax to Optimize Immutable Types
PEP 841 – Adding Frozen Syntax to Optimize Immutable Types
Author:<br>Donghee Na ,<br>Nikita Sobolev<br>Discussions-To:<br>Discourse thread<br>Status:<br>Draft<br>Type:<br>Standards Track<br>Created:<br>20-Jul-2026<br>Python-Version:<br>3.16<br>Post-History:<br>20-Jul-2026
Table of Contents<br>Abstract
Motivation
Rationale<br>Syntax, not a builtin call
Why f{...}
Specification<br>Grammar
AST
Semantics
Bytecode
The optimization pipeline
Backwards Compatibility
How to Teach This
Impact on the Standard Library
Reference Implementation
Rejected Ideas<br>Alternative spellings
Freezing methods
Copyright
Abstract
This PEP proposes frozen display syntax: f{1, 2, 3} evaluates to a<br>frozenset, and f{'a': 1} evaluates to a frozendict.<br>Because immutability is guaranteed by the syntax itself rather than<br>inferred from usage, the compiler can treat frozen displays as first-class<br>citizens of its optimization pipeline: constant displays are folded into a<br>single LOAD_CONST with an exact result type at compile time and<br>cached in .pyc files.
Motivation
Python has display syntax for its mutable containers but none for its<br>immutable ones. Today an immutable set must be written as<br>frozenset({1, 2, 3}) and an immutable mapping as<br>frozendict({'a': 1}). Each of these:
builds a mutable set or dict, then copies it into the immutable type.
looks up the name frozenset or frozendict at runtime on every<br>execution.
cannot be easily optimized by the compiler, because either name may be<br>rebound and the call may have arbitrary side effects, or<br>value can be of unexpected type for the optimization,<br>value can be non uniquely referenced.
CPython already hints at the opportunity: the peephole optimizer rewrites<br>a constant set display into a frozenset, but only as the right operand<br>of in. Assign the same display to a variable and the optimization is<br>gone. The root cause is that the compiler can never prove immutability<br>of a set or dict display, so it must rebuild it on every<br>execution. A display whose semantics guarantee immutability removes<br>that barrier once and for all.
One of the goals of this PEP is to increase the use of immutable containers<br>in CPython, preparing for a concurrent future in which free-threading and<br>subinterpreters become significantly more common.<br>Efficient creation of immutable data structures and convenient syntax would<br>encourage the use of frozendict and frozenset. This would reduce bugs<br>caused by accidental mutation of shared mutable containers while also<br>improving performance. For example, subinterpreters already share<br>frozenset objects, and there are plans to share frozendict objects as<br>well.
Immutable container displays have been requested and discussed by the<br>community several times, most recently in the frozenset and frozendict<br>comprehensions<br>thread on Discourse.
Rationale
Syntax, not a builtin call
Only syntax gives the compiler a semantic guarantee. A call to<br>frozenset(...) can be shadowed, but a frozen display cannot. Every<br>optimization described below follows from this single property.
Static analysis benefits in the same way. Today, tools<br>that don’t perform semantic analysis must assume<br>that frozenset(...) refers to the builtin. A frozen display turns<br>that assumption into a syntactic guarantee, so analyzers can treat the<br>result as immutable with full confidence. This holds even for purely<br>syntactic tools that perform no name resolution.
Linters and formatters can automatically rewrite frozenset({1, 2, 3})<br>and frozendict({1: 2}) to be f{1, 2, 3} and f{1: 2}<br>on newer Python versions.
Why f{...}
The f prefix reads as frozen, mirroring the familiar f-string<br>prefix convention. Sharing the letter with f-strings is not a problem:<br>strings are immutable too, so either way an f prefixed expression<br>evaluates to an immutable value. f{ is a syntax error in all<br>current Python versions, so the syntax is fully backward compatible. The tokenizer<br>emits a single FLBRACE token for f{, so f {1} (with a space)<br>remains an error and there is no ambiguity with the name f or with<br>f-strings.
Specification
Grammar
Our goal is to make new syntax and grammar identical<br>to existing set and dict syntax and grammar rules.
New alternatives are added to atom, mirroring set and dict<br>displays and comprehensions:
fset: FLBRACE star_named_expressions '}'<br>fsetcomp: FLBRACE star_named_expression for_if_clauses '}'<br>fdict: FLBRACE [double_starred_kvpairs] '}'<br>fdictcomp: FLBRACE kvpair for_if_clauses '}'
f{1, 2, 3} is a frozenset display.
f{'a': 1, 'b': 2} is a frozendict display.
f{} is an empty frozendict, mirroring {}.
Star unpacking follows the existing displays: f{*xs} is a<br>frozenset display (like {*xs}) and f{**d} is a frozendict<br>display (like {**d}).
Comprehensions are supported: f{x for x in xs} is a frozenset<br>comprehension and f{k: v...