Initializer for set, like dict {} and list []

Quite often I find myself using sets, and I cannot initialize them, like you can a dict, list or a tuple.
This makes uglier code:

a_dictionary = {}
a_tuple = ()
a_list = []
a_set = set()

Also set comprehension uses {} as a dict:

a_set = {'unique_1', 'unique_2'}

I propose that set gets the unique bracket style <>
Code will look like so:

a_dictionary = {}
a_tuple = ()
a_list = []
a_set = <>

And for comprehension it would look like so:

a_set = <'unique_1', 'unique_2'>

And this would make differentiating between a dict and a set easier, especially when searching across files.

1 Like

This has been discussed previously, (and it’s often mentioned in other discussions too):

You can {*()} as a workaround, although you do start to wonder if {*} would make sense.

5 Likes

Also, angle brackets (as a brackets) should be used only as last resort, because they exit in operators. For example, see isocpp.org article on right angle bracket change in C++11. Or just think of JavaScript in XHTML file.

Later language changes don’t use it, like Python’s type generics (typing.Generic), or C’s _BitInt(n). To use angle brackets today, U would need a very strong justification.

1 Like

I beg to differ. It’s only three characters longer. It requires no new syntax to be created and remembered. And it’s not only readable, but beautifully self-explanatory.

11 Likes

And it’s vulnerable to accidental shadowing of the name set, which is not the case for any other type.

Any proposal can be dismissed as merely “three characters shorter/longer” but that’s an irrelevant criticism. (Unless someone is actually advocating for this purely on the basis of it being shorter, which hasn’t happened yet.) We don’t value code on the basis of length, which means we also shouldn’t criticize code on the basis of length. Expressiveness and consistency are much more important.

1 Like

I still really like my {/} idea from one of the many previous threads, it looks like the empty set from math if you squint! But I’m fine with not having a language consteuct for this.

6 Likes

First, this breaks the current convention of using angle brackets for unrepresentable repr strings. Like, repr(42) is 42 and repr(datetime.date(2020, 12, 31)) is datetime.date(2020, 12, 31) because that’s the code to re-create those objects.

But sqlite3.connect('example.db') is <sqlite3.Connection object at 0x00000265674545E0> with angle brackets specifically because you can’t easily re-create a sqlite Connection object with a literal.

Second, this is the sort of “PERLification” change that makes the language less readable by trading words for punctuation symbols. This is a small change, but there are thousands of small changes like this that could be made and would result in a language full of cryptic-looking symbols.

Third, for example, if this change were adopted people would also want another change for frozen sets. How would you do this? Double angle brackets? So <> is a set and <<>> is a frozen set? Or should it be <<>> is a set and <> is a frozen set? You can see how this makes code unreadable.

EDIT: And what if you wanted an expression as part of the set? For example, you could have the dictionary {'of_age': age > 18}, but if you wanted to do that with this syntax, you’d end up with <age > 18>

9 Likes

Another issue with angle brackets is that it would be inconsistent with set literals and comprehensions, which use curly brackets. It would be odd to use <> for an empty set and {1} for a single-element one.

A less concrete issue is that it precludes angle brackets being used in the future for something more useful than this (I think this is fairly low-impact).

1 Like

True. Python has plenty of other built-ins that can also be shadowed, but I wouldn’t have thought of that. Thanks.

Personally I would prefer to deprecate [], {}, and () as a way of initializing and empty list, dict, and tuple. As a general rule I initialize them with calls to list(), dict(), and tuple() to make sure there is no confusion.
As to comprehensions, {n for n in range(10)} is clearly a set and {str(n): n for n in range(10)} is clearly a dict. I don’t think there is any benefit in changing/adding a new way to do a set comprehension.

1 Like

Do you also construct an empty string with str() rather than using the literal "" ? This all seems quite unnecessary - literals [1] exist for a reason. They are not confusing once you know that square brackets make a list.

If anything, the distinction between a set comprehension and a dict comprehension is far slimmer; so if that is sufficient, there should be no confusion with [] for a new empty list.


  1. and “display” syntax which isn’t technically a literal ↩︎

4 Likes

I agree with you, but what should be the syntax? I really don’t like to have alternative brackets for set, and I can’t imagine a different solution.

String literals are in pretty much every language. Set literals aren’t.

2 Likes

The answer here is likely that if Python was redesigned today {} would be an empty set and {:} would be an empty dict, but we cannot do that anymore so sets will just have to be a bit less ergonomic than dicts. {*()} is a reasonable workaround.

2 Likes

List/array literals are in pretty much every language. I was responding to @brass75 's idea that [] is worse than list() which IMO is very much backwards.

We have syntax for everything except the empty set. I would be all in favour of an “empty set display” dedicated syntax, if a good one can be found; having these sorts of things as syntax is valuable for readability, and can improve performance too (not that I’ve ever had that be a problem, but at least it won’t worsen it).

Maybe {*()} will do, but it feels a bit like a hack to me.

1 Like

How do you create a list with one element? How with two?

1 Like

I specifically said “empty”.

1 Like

And I’m curious what you do for one or two.

1 Like

This looks less readable (as in too many symbols for one simple construct) than set() though.

That said, I’d probably swallow the ugliness if the compiler would optimize away the LOAD_CONST () and SET_UPDATE combo:

import dis
def f():
    return {*()}
dis.dis(f)

which currently produces:

  2           RESUME                   0

  3           BUILD_SET                0
              LOAD_CONST               1 (())
              SET_UPDATE               1
              RETURN_VALUE

The already exists a symbol for an empty set. If Python were to add a literal for empty sets, at least it should use standard notation:

a_set = ∅

IMO a little unicode is better than forcing angle brackets to mean something that no one expects.

1 Like