
On Mon, Jan 17, 2022 at 4:38 PM Christopher Barker <pythonchb@gmail.com> wrote:
On Sun, Jan 16, 2022 at 6:34 PM Chris Angelico <rosuav@gmail.com> wrote:
def fun(): return "some string"
doesn't return the same string, unless it's iterned, which is an implementation detail, yes?
Not sure what you mean. That's a constant, so it'll always return the exact same object, surely?
I *think* that's only if it's interned -- and in any case, is a guarantee of the language, or an optimization?
I tried to test with a longer string, and it was the same one, but then I found in this arbitrary post on the internet:
... in Python 3.7, this has been changed to 4096 characters
( I guess I haven't played with that since 3.7) -- I haven't actually tried with a string literal linger than 4096 chars :-)
When you return a literal, like that, the literal becomes a function constant. That's not necessarily a language guarantee, but I would be hard-pressed to imagine a Python implementation that copied it for no reason.
But this certainly doesn't:
In [1]: def fun(): ...: return [1,2,3] ...:
In [2]: l1 = fun()
In [3]: l2 = fun()
In [4]: l1 is l2 Out[4]: False
So the issue is immutability and interning, not "literal display".
The difference is that list display isn't a literal, but a simple quoted string is. The square brackets are guaranteed to construct a new list every time.
My point is that a frozenset litteral could open the door to interning frozen sets, but that could probably be done anyway. And why? Are they heavily used in any code base?
A frozenset literal would allow the same frozenset to be reused, but it's not about interning. You're right that interning would be possible even without a literal, but in order to intern frozensets without a literal, we'd still need to construct a brand new (non-frozen) set to pass to the constructor, since there needs to be a way to indicate which elements we want. Technically, there are no "complex literals", but thanks to constant folding, a value like 3+4j can become a function constant. In theory, I could imagine a Python implementation that treats 3 and 4j as constants, but adds them at runtime, producing a unique complex object every time. But since there are no shadowable name lookups, it's easy enough to take advantage and gain a nice optimization. In theory, what could be done for frozensets would be to use a *tuple* for the arguments, which can then be used for an intern-style lookup: _frozenset = frozenset _fscache = {} def frozenset(t): if type(t) is tuple: # strict check, don't allow subclasses if t not in _fscache: _fscache[t] = _frozenset(t) return _fscache[t] return _frozenset(t) This would remove some of the copying, but at the cost of retaining all the tuples (in other words: now you have a classic caching problem). Could be useful for situations where it really truly is constant, but it's still a bit of a roundabout way to do things. A true frozenset literal can't possibly be shadowed, so the compiler would be free to do all the same optimizations that it does with tuples. ChrisA