On Fri, 21 Jan 2022 at 22:52, Oscar Benjamin <oscar.j.benja...@gmail.com> wrote:
>
> On Thu, 20 Jan 2022 at 10:19, Ricky Teachey <ri...@teachey.org> wrote:
>>
>> On Thu, Jan 20, 2022 at 3:35 AM Stephen J. Turnbull 
>> <stephenjturnb...@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.
>

If set.frozen() is optimized, then str.upper() can be optimized the
same way, which means there's a lot of places where constant folding
can be used. We commonly write code like "7*24*60*60" to mean the
number of seconds in a week, confident that it'll be exactly as fast
as writing "604800", and there's no particular reason that method
calls can't get the same optimization, other than that it hasn't been
done yet.

While dedicated syntax might be as good, it also wouldn't help with
string methods (or int methods - I don't see it a lot currently, but
maybe (1234).to_bytes() could become more popular), and it would also
be completely backward incompatible - you can't feature-test for
syntax without a lot of hassle with imports and alternates. In
contrast, code that wants to use set.frozen() can at least test for
that with a simple try/except in the same module.

Not one of the proposed syntaxes has seen any sort of strong support.
This isn't the first time people have proposed a syntactic form for
frozensets, and it never achieves sufficient consensus to move
forward.

> 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:
>

"Clearly" is subjective. Any syntax could be used for repr, including
{1,2,3}.frozen(), so f{1,2,3} doesn't have any particular edge there.
Personally, I think that string literals are not the same thing as
tuple/list/dict/set displays, and letter prefixes are not as useful on
the latter.

>   >>> 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?

Yes, it most certainly would change the repr. I don't see why that's an issue.

> 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.:
>

I don't understand polynomials as frozensets. What's the point of
representing them that way? Particularly if you're converting to and
from dicts all the time, why not represent them as dicts? Or as some
custom mapping type, if you need it to be hashable?

>     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.
>

Well, yes. This sort of code isn't what's being optimized here.
Massaging data between different formats won't be enhanced by a
literal form.

> 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)
>

Again, not the point of the literal form. There's no significant
difference between a set and a frozenset when testing for inclusion,
so you're testing something meaningless here.

The point of a literal form is that it is guaranteed to mean what you
intend it to mean. That's why we (usually) use {"a":1, "b":2} rather
than dict(a=1, b=2) - not because it's faster (it is, but not by that
big a margin), but because it doesn't depend on the name dict. Maybe
that's not important to your code. That's fine. Not every feature has
to benefit every programmer. A frozenset display syntax would only
benefit me in a few places. But the benefits aren't where you're
looking for them, so naturally you're not going to find them there. :)

ChrisA
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/Y45UCQZ2GW62SVRNGKD2WR57W54LKCY2/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to