
On Thu, 20 Jan 2022 at 10:19, Ricky Teachey <ricky@teachey.org> wrote:
On Thu, Jan 20, 2022 at 3:35 AM Stephen J. Turnbull < stephenjturnbull@gmail.com> wrote:
Christopher Barker writes:
If this does all come to pass, then:
s = {3,8,2}.frozen() will be slightly faster, in some case, than s = frozenset({3,8,2}
but the result would be the same.
There are plenty of tricks to in python to get a touch more performance, this would just be one more and frankly pretty rare that it would make an noticable difference at all.
+1 on this +0 on f{} -1 on making frozenset a keyword
Stated better than I could, expresses my feelings exactly. Sticking to integers (in floats I'd be less than -0 on f{}), I'll go with Chris's ratings, too.
Steve
Another agreement with Chris' ratings:
+1 for .frozen() +0 on f{} -1 on keyword for frozenset
I really don't understand (having read everything above) why anyone prefers {1,2,3}.frozen() over f{1,2,3}. Yes, some people coming from some other languages might get confused (e.g. in Mathematica this is function call syntax) but that's true of anything: you have to learn Python syntax to use Python. The fact that {1,2,3} is a set and f{1,2,3} is a frozenset is not difficult to explain or to understand, especially in a language that already uses single letter prefixes for other things. The .frozen() method is a strangely indirect way to achieve a minor optimisation. Outside of attempting to achieve that optimisation it's basically useless because any time you would have written obj.frozen() you could have simply written frozenset(obj) so it does nothing to improve code that uses frozensets. With f{...} you have a nice syntax that clearly creates a frozenset directly and that can be used for repr. This is some actual code that I recently wrote using frozensets to represent monomials in a sparse representation of a multivariate polynomial:
poly = {frozenset([(1,2), (3,4)]): 2, frozenset([(0,1)]): 3} poly {frozenset({(1, 2), (3, 4)}): 2, frozenset({(0, 1)}): 3}
With the f{...} proposal you have actual syntax for this:
poly = {f{(1,2), (3,4)}: 2, f{(0,1)}): 3} poly {f{(1, 2), (3, 4)}: 2, f{(0, 1)}): 3}
With .frozen() it's
poly = {{(1,2), (3,4)}.frozen(): 2, f{(0,1)}.frozen()): 3} poly ??? does the repr change?
That difference in code/repr may or may not seem like an improvement to different people but that should be the real point of discussion if talking about a frozenset literal. The performance impact of frozenset literals is not going to be noticeable in any real application. My polynomial class makes extensive use of frozensets and is something that I do need to be as fast as possible. I just looked through the code I have for that class and none of the performance sensitive routines could benefit from this because they all actually need to build their elements in a dict before converting to a frozenset anyway e.g.: def mul(self, other): """multiply two (frozenset) monomials""" powermap = dict(self) for g, n in other: other_n = powermap.get(g) if other_n is None: powermap[g] = n else: powermap_n = other_n + n if powermap_n: powermap[g] = powermap_n else: powermap.pop(g) return frozenset(powermap.items()) I've just profiled this and the call to frozenset is always dwarfed by the time taken in the preceding loop which shows how cheap converting between builtins is compared to pretty much any other code. If you're using literals then of necessity you are talking about small sets. Even just using a small set over a small tuple is a hardly noticeable difference in speed in most situations: In [12]: s = {1,2,3} In [13]: t = (1,2,3) In [14]: timeit 2 in s 44.9 ns ± 0.17 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) In [15]: timeit 2 in t 59.9 ns ± 5.67 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) -- Oscar