# \`frozenset\` and \`frozendict\` comprehensions

**URL:** <https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584>\
**Category:** Ideas\
**Created:** [August 7, 2025, 6:32pm UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584 "2025-08-07T18:32:59Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![dg-pb](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/dg-pb/32/16248_2.png) [@dg-pb](https://discuss.python.org/u/dg-pb)\
**Post date:** [August 7, 2025, 6:32pm UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/1 "2025-08-07T18:32:59Z")

</div>

### 1. Introduction

Potentially complete core container set:

1. `list` & `tuple`
2. `set` & `frozenset`
3. `dict` & `frozendict`

Given the above, these are “missing”:

1. `set` - no empty set literal
2. `frozenset` - object exists, but nothing else
3. `frozendict` - nothing exists, not even the object

To address (1), there are couple of threads:

1. [Initializer for set, like dict {} and list []](https://discuss.python.org/t/initializer-for-set-like-dict-and-list/73235)
2. [Implement {,} as an empty set literal](https://discuss.python.org/t/implement-as-an-empty-set-literal/101197)

To address (2), has been one attempt (as far as I know):

1. [Frozenset literals and comprehensions](https://discuss.python.org/t/frozenset-literals-and-comprehensions/88098)

To address (3), the first step of object creation:

1. [PEP 603: Adding a frozenmap type to collections](https://discuss.python.org/t/pep-603-adding-a-frozenmap-type-to-collections/2318)
2. I think there have been more attempts, but it is not relevant to this thread.

### 2. Consideration

(1) above is just for complete picture, but this is mostly about (2) and (3).

My thought process:

1. So (2) was attempted and proved to be tricky (at least that specific solution).
2. If `frozendict` was implemented, then instead of 1 full set of literals/comprehensions, 2 are lacking. And even one seems to be difficult.
3. Thus, maybe what could work is something similar to “string type prefixes”?

Given set literal exists. E.g. `{/}`:

```python
{/} # Empty Set
f{/} # Empty Frozen Set

{} # Empty Dict
f{} # Empty Frozen Dict

{a for a in iterable} # set
f{a for a in iterable} # frozenset

{a: b for a, b in iterable} # dict
f{a: b for a, b in iterable} # frozendict

```

Not necessarily `f`, but the idea is _not_ to make up new `brackets`, but instead use the same syntax, but with some `frozen` indicator. Maybe prefix, maybe suffix, maybe something else entirely.

### 3. To sum up

If `frozendict` is indeed coming and comprehensions for both `frozenset` and `frozendict` are desirable, then solution that would capture comprehensions/literals for them both while drawing sensible/consistent parallels could be convenient/smooth path forward.

Thoughts?

---

<div class="post-metadata">

**Author:** ![Wombat](https://avatars.discourse-cdn.com/v4/letter/w/e480ec/32.png) [@Wombat](https://discuss.python.org/u/Wombat)\
**Post date:** [August 7, 2025, 11:37pm UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/2 "2025-08-07T23:37:25Z")

</div>

> [@dg-pb](#):
>
> `f{a for a in iterable} # frozenset`

I’m happy with the current form: `frozenset({a for a in iterable})`. That is clear and reasonably fast.

---

<div class="post-metadata">

**Author:** ![skirpichev](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/skirpichev/32/10996_2.png) [@skirpichev](https://discuss.python.org/u/skirpichev)\
**Post date:** [August 8, 2025, 3:09am UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/3 "2025-08-08T03:09:41Z")

</div>

And “{}” are redundant here, just `frozenset(iterable)` or `frozenset(_.doit() for _ in iterable)`.

But I agree, there are clear workarounds without need for a new syntax, and this proposal lacks “Motivation“ section for lazy people like me, who (sometimes) don’t read previous discussions.

---

<div class="post-metadata">

**Author:** ![Tinche](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/tinche/32/12507_2.png) [@Tinche](https://discuss.python.org/u/Tinche)\
**Post date:** [August 8, 2025, 9:48am UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/4 "2025-08-08T09:48:47Z")

</div>

I like it!

---

<div class="post-metadata">

**Author:** ![Stefan2](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/stefan2/32/18492_2.png) [@Stefan2](https://discuss.python.org/u/Stefan2)\
**Post date:** [August 8, 2025, 11:20am UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/5 "2025-08-08T11:20:03Z")

</div>

It seems what you mean with “literals” differs from the [documentation](https://docs.python.org/3/reference/lexical_analysis.html#literals) (it’s just strings and some numbers, not even `()`).

---

<div class="post-metadata">

**Author:** ![dg-pb](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/dg-pb/32/16248_2.png) [@dg-pb](https://discuss.python.org/u/dg-pb)\
**Post date:** [August 8, 2025, 11:42am UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/6 "2025-08-08T11:42:27Z")

</div>

I am probably wrong with precise terminology.

I just mean “_syntax_ for shorthand empty container and comprehensions”. However it is defined in strict terms.

---

<div class="post-metadata">

**Author:** ![AndersMunch](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/andersmunch/32/12823_2.png) [@AndersMunch](https://discuss.python.org/u/AndersMunch)\
**Post date:** [August 8, 2025, 12:52pm UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/7 "2025-08-08T12:52:56Z")

</div>

I’m gravitating towards the opposite point of view: That Python has too many comprehension variants already. That all we need is generator expressions passed to constructors.

---

<div class="post-metadata">

**Author:** ![sobolevn](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/sobolevn/32/9274_2.png) [@sobolevn](https://discuss.python.org/u/sobolevn)\
**Post date:** [June 29, 2026, 12:15pm UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/8 "2026-06-29T12:15:40Z")

</div>

I really like this idea, I was going to propose adding literally the same thing 🙂

It looks like a such tiny change that will unblock so many useful features. However, there is a downside to this. `f{}` looks very close to `f'{}'`, which are two completely different things.

It will also require some refactorings of `INTRINSIC_BUILD_FROZENSET` 🤔

I would really want to push for this change. Working with frozendict and frozenset right now is not really fun ☹

---

<div class="post-metadata">

**Author:** ![sobolevn](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/sobolevn/32/9274_2.png) [@sobolevn](https://discuss.python.org/u/sobolevn)\
**Post date:** [June 29, 2026, 2:17pm UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/9 "2026-06-29T14:17:59Z")

</div>

Updated proposal:

`frozenset`

- Empty: `frozenset()`
- With items: `${1, 2}`
- Comprehension: `${x for x in range(10)}`

`frozendict`

- Empty: `frozendict()`
- With items: `${1: 2, 3: 4}`
- Comprehension: `${x: 1 for x in range(1)}`

Main motivation:

- Simplicty: we want to endorse immutable objects usage, especially for free-threading and subinterpreters. Simple syntax propomotes this
- Performance: we don’t want to create intermediate objects to create a builtin type. Special syntax allows to optimize this perfectly

In my opinion there are several important questions if we want to start working on this.

1. I decided to go with `$` (because immutablity is the real deal). It does not look similar to anything else, like `f{}` looks similar to `f''`, it can be scaled to `$(x for x in range(2))` which would mean tuple comprehension, if we would want this. It can be also added to other data types in the future. While `f` can’t be used in cases like `f[]` and `f()`, because it is a valid syntax already.
2. Should we really go for empty object literals? Looks like `set()`, `frozenset()` and `frozendict()` calls can be good enough as the empty objects. I propose limiting the scope of this proposal to literals and comprehensions only. We can later address the empty objects separately in the next proposal. It would be easier to get the approvals for the smallest piece of new syntax 🙂
3. How it should be implemented technically. Currently, there’s an optimization built on top if `INTRINSIC` for `INTRINSIC_BUILD_FROZENSET`. Should we build new opcodes or go for `INTRINSIC`? Should we plan for any future optimizations beforehand?
4. What C-API would we need? This is a very hot topic for `PyFrozenDict` API. Currently it does not have a way to build objects interactively. So, we would first likely need to add `PyFrozenDict_FromDictSteal` API to make a dict with existing APIs and convert it to `frozendict` the most efficient way. This is the case because `PyDict_*` mutation APIs can’t be used with `PyFronzenDict`. The same is not the case for `frozenset`, it can use mutating `PySet_*` APIs.
5. Analyze potential syntax conflicts in lexer / parser with `f` prefix for strings / frozen objects (if we decide to go with this prefix)
6. Provide perf numbers: how much faster we can optimize the workflow with our design, compared to older forms like `frozendict({1: 2})` and `frozenset(x for x in range(10))`
7. Find other core devs who would like and support this idea 🙂

Everyone’s feedback is welcome! 👋

---

<div class="post-metadata">

**Author:** ![encukou](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/encukou/32/2461_2.png) [@encukou](https://discuss.python.org/u/encukou)\
**Post date:** [June 29, 2026, 3:06pm UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/10 "2026-06-29T15:06:10Z")

</div>

Does it need new syntax? AFAIK, a method like `{1, 2, 3}.freeze()` would be just as optimizable.

---

<div class="post-metadata">

**Author:** ![sergey-miryanov](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/sergey-miryanov/32/27486_2.png) [@sergey-miryanov](https://discuss.python.org/u/sergey-miryanov)\
**Post date:** [June 29, 2026, 7:07pm UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/11 "2026-06-29T19:07:58Z")

</div>

`{1, 2, 3}.freeze()`

You still need to construct a temporary copy before freezing a dictionary in this case. Or we need a `PyFrozenDict_FromDictSteal` API.

---

<div class="post-metadata">

**Author:** ![tstefan](https://avatars.discourse-cdn.com/v4/letter/t/f475e1/32.png) [@tstefan](https://discuss.python.org/u/tstefan)\
**Post date:** [June 29, 2026, 7:52pm UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/12 "2026-06-29T19:52:18Z")

</div>

> [@sergey-miryanov](#):
>
> You still need to construct a temporary copy

Since you cannot change attributes of a `dict` or a `set`, it is already known at compile-time what `{1,2,3}.freeze()` means. So, turning it into a frozenset is essentially constant folding and could be done either as an AST optimization or during the code generation phase.

---

<div class="post-metadata">

**Author:** ![sobolevn](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/sobolevn/32/9274_2.png) [@sobolevn](https://discuss.python.org/u/sobolevn)\
**Post date:** [June 29, 2026, 8:57pm UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/13 "2026-06-29T20:57:12Z")

</div>

I don’t quite like this solution for several reasons:

1. It will be much more verbose: `some(f{1, 2, 3}, f{4, 5})` vs `some({1, 2, 3}.freeze(), {4, 5}.freeze())`
2. It will require us to add `.freeze` to many `dict`-like and `set`-like types like `UserDict`, do we actually need them on these types?
3. It is harder to read. If a dict literal goes on several lines, you will only learn the type on the very last line, which is counter intuitive. For example:

```python
data = { # what is the type of `data`? is it `dict` or `frozendict`?
    "first": 1,
    "second": 2,
    "third": 3,
    # we still don't know the type :(
    "one more": ...,
    "last": ...,
}.freeze() # oh, it is `frozendict`!

```

So, yeah. Syntax seems like a better solution here 🙂

---

<div class="post-metadata">

**Author:** ![timhoffm](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/timhoffm/32/19468_2.png) [@timhoffm](https://discuss.python.org/u/timhoffm)\
**Post date:** [June 30, 2026, 5:52am UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/14 "2026-06-30T05:52:23Z")

</div>

> [@sobolevn](#):
>
> many useful features. However, there is a downside to this. `f{}` looks very close to `f'{}'`, which are two completely different things.

Has `frozen{}` been considered? It‘s not as compact as `f{}`, but on the plus side it literally reads „frozen dict“, and it’s still shorter than the current `frozendict({})`.

---

<div class="post-metadata">

**Author:** ![tstefan](https://avatars.discourse-cdn.com/v4/letter/t/f475e1/32.png) [@tstefan](https://discuss.python.org/u/tstefan)\
**Post date:** [June 30, 2026, 6:13am UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/15 "2026-06-30T06:13:51Z")

</div>

I think the `f` was chosen to mirror the string prefixes (which are single characters as well). It would be also my preferred option if a new syntax is introduced. The word `frozen` in `frozen{1,2,3}` looks more like a name, which is misleading since no name lookup happens here.

---

<div class="post-metadata">

**Author:** ![JoBe](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jobe/32/26961_2.png) [@JoBe](https://discuss.python.org/u/JoBe)\
**Post date:** [June 30, 2026, 8:51am UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/16 "2026-06-30T08:51:53Z")

</div>

3 could in theory be helped with by type hints.

However I also dislike the `.freeze()`, especially as it would have to be special cased by constant-folding or code-gen. Although I think `f{}` is (too?) close to f-strings, I currently don’t really see an alternative to it. Therefore I’d rather support `f{}` unless a better alternative is found.

---

<div class="post-metadata">

**Author:** ![tstefan](https://avatars.discourse-cdn.com/v4/letter/t/f475e1/32.png) [@tstefan](https://discuss.python.org/u/tstefan)\
**Post date:** [June 30, 2026, 11:01am UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/17 "2026-06-30T11:01:38Z")

</div>

> [@JoBe](#):
>
> However I also dislike the `.freeze()`, especially as it would have to be special cased by constant-folding or code-gen.

That would be only an implementation detail, like all the other constant-folding code that is already in place. And it would not even need to be a special case; the same code could also optimize code like `"abc".upper()`, which is not optimized at the moment.

> **Test case**
>
> ```python
> Python 3.13.14
> >>> import dis
> >>> def foo():
> ... return "abc".upper()
> ...     
> >>> dis.dis(foo)
> 1 RESUME 0
> 
> 2 LOAD_CONST 1 ('abc')
> LOAD_ATTR 1 (upper + NULL|self)
> CALL 0
> RETURN_VALUE
> >>> def bar():
> ... return 2+3
> ...     
> >>> dis.dis(bar)
> 1 RESUME 0
> 
> 2 RETURN_CONST 1 (5)
> 
> ```

---

<div class="post-metadata">

**Author:** ![sobolevn](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/sobolevn/32/9274_2.png) [@sobolevn](https://discuss.python.org/u/sobolevn)\
**Post date:** [June 30, 2026, 11:16am UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/18 "2026-06-30T11:16:12Z")

</div>

> 3 could in theory be helped with by type hints.

It would still require you:

- To add `.freeze()`
- To add a typehint

Moreover, there are places where you can’t add a hint. For example in a function call argument.

I think that a new syntax a much more ergonomic and readable. It is also not much harder to implement and support. I have a small draft already. PR with the proposal will soon be available 🙂

---

<div class="post-metadata">

**Author:** ![sobolevn](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/sobolevn/32/9274_2.png) [@sobolevn](https://discuss.python.org/u/sobolevn)\
**Post date:** [June 30, 2026, 12:34pm UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/19 "2026-06-30T12:34:07Z")

</div>

One more thing: we can reliably optimize dict literal + `.freeze()` call: `{}.freeze()`, however we can only optimize statistically `d = {}; call(d.freeze())` if this pattern is used for some reason by the end user. We would have to teach people that it is not the same thing. And there will different runtime optimizations based on whether you use literal form vs variable name. While, it is quite obvious that `{1, 2}` and `f{1, 2}` are different: because they use different syntax.

---

<div class="post-metadata">

**Author:** ![blhsing](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/blhsing/32/25812_2.png) [@blhsing](https://discuss.python.org/u/blhsing)\
**Post date:** [June 30, 2026, 3:41pm UTC](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/20 "2026-06-30T15:41:03Z")

</div>

I can live with the `f{...}` syntax but wish that `name{...}` can be reserved for something more generalized like [this](https://discuss.python.org/t/add-a-new-token-to-force-the-methods-to-returns-the-previous-object/105261/60).

I would instead propose that we make the unary `+` operator “harden” (that’s how I’d teach it–`+` ~ positivity ~ fortitude ~ hardened ~ frozen) a set or a dict:

```python
{/} # Empty Set
+{/} # Empty Frozen Set at compile time
+set() # Empty Frozen Set at runtime

{} # Empty Dict
+{} # Empty Frozen Dict at compile time
+dict() # Empty Frozen Dict at runtime

{a for a in iterable} # set
+{a for a in iterable} # frozenset

{a: b for a, b in iterable} # dict
+{a: b for a, b in iterable} # frozendict

```

The usage is concise, does not require a new syntax, and can be easily optimized at compile time.

[Next page](https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584.md?page=2)
