New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generate frozenset constants when explicitly appropriate #90551
Comments
The CPython compiler is capable of making frozenset constants without being explicitly asked to. Exactly how it does so is, of course, 'hidden' from python code. With current main:
Suppose one wants actually wants a frozenset, not a mutable set. 'frozenset({1,2,3})' is compiled as the above followed by a frozenset call -- making an unneeded double conversion to get what already exists.
Even nicer would be
'set((1,2,3))' is compiled the same as 'frozenset((1,2,3)), but frozenset does not have the option of using a more efficient display form. I cannot think of any reason to not call frozenset during compile time when the iterable is a constant tuple. Serhiy, I not sure how this relates to your bpo-33318 and the discussion therein about stages, but it does relate to your interest in compile time constants. |
The difficulty is that frozenset may have been shadowed, or builtins monkey-patched, so we cannot know what frozenset({1, 2, 3}) will return until runtime. Should we re-consider a frozenset display literal?
works for me. |
Sigh. You are right. I will close this tomorrow. This also means that 'set()' is not guaranteed to return an empty built-in set. I did think of this workaround for that:
>>> (empty:={None}).clear()
>>> empty
set()
Go ahead and propose something on python-ideas if you want, pointing out that only displays (and comprehensions) are guaranteed to result in a builtin. |
There's also the hacky expression {*()} to get an empty set |
Proposed on Python-Ideas. |
[Terry]
It's not quite as bad as that: there _is_ no intermediate set (or if you prefer, the intermediate set is the same object as the final set), since the frozenset call returns its argument unchanged if it's already of exact type frozenset: >>> x = frozenset({1, 2, 3})
>>> y = frozenset(x)
>>> y is x
True Relevant source: Lines 999 to 1003 in 09087b8
|
That's not always the case though. >>> def f():
... return frozenset({1, 2, 3})
...
>>> a = f.__code__.co_consts[1]
>>> a
frozenset({1, 2, 3})
>>> b = f()
>>> assert a == b
>>> a is b
False Also see the disassembly I posted on Python-Ideas. |
Sorry, yes - I see. We're not creating a frozenset from a frozenset - we're creating a frozenset from a regular set from a frozenset. :-( Sorry for the noise. |
Rejected by the reality of Python's dynamism, which I overall appreciate ;-). |
As Steven have noted the compiler-time optimization is not applicable here because name frozenset is resolved at run-time. In these cases where a set of constants can be replaced with a frozenset of constants (in "x in {1,2,3}" and in "for x in {1,2,3}") the compiler does it. And I don't think there is an issue which is worth changing the language. Creating a frozenset of constants is pretty rare, and it is even more rare in tight loops. The most common cases (which are pretty rare anyway) are already covered. |
I cleaned up the 'code' examples and the next to last sentence, verified that 'x in {1,2,3}' indeed uses a precompiled frozen set constant, and agree with Serhiy that no change is needed. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: