Skip to content

Commit 4d9ec8b

Browse files
committed
Implement PEP 802 and the {/} notation
1 parent 34d7351 commit 4d9ec8b

File tree

22 files changed

+468
-215
lines changed

22 files changed

+468
-215
lines changed

Doc/library/stdtypes.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ objects considered false:
5858
* zero of any numeric type: ``0``, ``0.0``, ``0j``, ``Decimal(0)``,
5959
``Fraction(0, 1)``
6060

61-
* empty sequences and collections: ``''``, ``()``, ``[]``, ``{}``, ``set()``,
61+
* empty sequences and collections: ``''``, ``()``, ``[]``, ``{}``, ``{/}``,
6262
``range(0)``
6363

6464
.. index::
@@ -4668,6 +4668,7 @@ The constructors for both classes work the same:
46684668

46694669
Sets can be created by several means:
46704670

4671+
* Use a slash within braces, for the empty set: ``{/}``
46714672
* Use a comma-separated list of elements within braces: ``{'jack', 'sjoerd'}``
46724673
* Use a set comprehension: ``{c for c in 'abracadabra' if c not in 'abc'}``
46734674
* Use the type constructor: ``set()``, ``set('foobar')``, ``set(['a', 'b', 'foo'])``

Doc/reference/datamodel.rst

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -450,11 +450,14 @@ There are currently two intrinsic set types:
450450

451451
Sets
452452
.. index:: pair: object; set
453-
454-
These represent a mutable set. They are created by the built-in :func:`set`
455-
constructor and can be modified afterwards by several methods, such as
456-
:meth:`~set.add`.
457-
453+
pair: empty; set
454+
455+
The items of a set are arbitrary Python objects.
456+
Sets are formed by placing a comma-separated list of expressions in
457+
curly brackets. For sets of length 1, the comma may be ommitted.
458+
An empty set can be formed by ``{/}``.
459+
Sets are mutable (see section :ref:`set`) and can be modified afterwards
460+
by several methods, such as :meth:`~set.add`.
458461

459462
Frozen sets
460463
.. index:: pair: object; frozenset

Doc/reference/expressions.rst

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -356,25 +356,25 @@ Set displays
356356
.. index::
357357
pair: set; display
358358
pair: set; comprehensions
359+
pair: empty; set
359360
pair: object; set
361+
single: {/}; set display
360362
single: {} (curly brackets); set expression
361363
single: , (comma); expression list
364+
single: / (fowarard slash); in set displays
362365

363366
A set display is denoted by curly braces and distinguishable from dictionary
364367
displays by the lack of colons separating keys and values:
365368

366369
.. productionlist:: python-grammar
367-
set_display: "{" (`flexible_expression_list` | `comprehension`) "}"
370+
set_display: "{" ("/" | `flexible_expression_list` | `comprehension`) "}"
368371

369372
A set display yields a new mutable set object, the contents being specified by
370-
either a sequence of expressions or a comprehension. When a comma-separated
371-
list of expressions is supplied, its elements are evaluated from left to right
372-
and added to the set object. When a comprehension is supplied, the set is
373-
constructed from the elements resulting from the comprehension.
374-
375-
An empty set cannot be constructed with ``{}``; this literal constructs an empty
376-
dictionary.
377-
373+
either a slash (for the empty set), a sequence of expressions, or a comprehension.
374+
When a comma-separated list of expressions is supplied, its elements
375+
are evaluated from left to right and added to the set object.
376+
When a comprehension is supplied, the set is constructed from
377+
the elements resulting from the comprehension.
378378

379379
.. _dict:
380380

Doc/tutorial/datastructures.rst

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -450,9 +450,11 @@ with no duplicate elements. Basic uses include membership testing and
450450
eliminating duplicate entries. Set objects also support mathematical operations
451451
like union, intersection, difference, and symmetric difference.
452452

453-
Curly braces or the :func:`set` function can be used to create sets. Note: to
454-
create an empty set you have to use ``set()``, not ``{}``; the latter creates an
455-
empty dictionary, a data structure that we discuss in the next section.
453+
Curly braces or the :func:`set` function can be used to create sets.
454+
``{/}`` is a special case that means the empty set, similar to :math:`\emptyset`.
455+
456+
.. hint:: ``{}`` will create an empty dictionary, not an empty set.
457+
We will discuss this data structure in the next section.
456458

457459
Here is a brief demonstration::
458460

Doc/whatsnew/3.15.rst

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,33 @@ Summary --- release highlights
7070
New features
7171
============
7272

73+
.. _whatsnew315-pep-802:
74+
75+
Display Syntax for the Empty Set
76+
--------------------------------
77+
78+
There is a new notation for the empty set, ``{/}``.
79+
As part of this, the :func`:str` and :func:`repr` of an empty :class:`set`
80+
will now return ``'{/}'``.
81+
82+
This complements the existing notation for empty tuples, lists, and
83+
dictionaries, which use ``()``, ``[]``, and ``{}`` respectively.
84+
85+
.. code-block:: python
86+
87+
>>> type({/})
88+
<class 'set'>
89+
>>> {/} == set()
90+
True
91+
>>> repr({/})
92+
'{/}'
93+
>>> str(set())
94+
'{/}'
95+
>>> str({/}) == str(set()) == repr({/}) == repr(set())
96+
True
97+
98+
(Contributed by Adam Turner in :pep:`802`.)
99+
73100
.. _whatsnew315-sampling-profiler:
74101

75102
High frequency statistical sampling profiler

Grammar/python.gram

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -993,13 +993,17 @@ strings[expr_ty] (memo):
993993
| a[asdl_expr_seq*]=tstring+ { _PyPegen_concatenate_tstrings(p, a, EXTRA) }
994994

995995
list[expr_ty]:
996+
| invalid_list_slash
996997
| '[' a=[star_named_expressions] ']' { _PyAST_List(a, Load, EXTRA) }
997998

998999
tuple[expr_ty]:
1000+
| invalid_tuple_slash
9991001
| '(' a=[y=star_named_expression ',' z=[star_named_expressions] { _PyPegen_seq_insert_in_front(p, y, z) } ] ')' {
10001002
_PyAST_Tuple(a, Load, EXTRA) }
10011003

1002-
set[expr_ty]: '{' a=star_named_expressions '}' { _PyAST_Set(a, EXTRA) }
1004+
set[expr_ty]:
1005+
| '{' '/' '}' { _PyAST_Set(NULL, EXTRA) }
1006+
| '{' a=star_named_expressions '}' { _PyAST_Set(a, EXTRA) }
10031007

10041008
# Dicts
10051009
# -----
@@ -1572,3 +1576,11 @@ invalid_type_params:
15721576
RAISE_SYNTAX_ERROR_STARTING_FROM(
15731577
token,
15741578
"Type parameter list cannot be empty")}
1579+
1580+
invalid_list_slash:
1581+
| '[' a='/' ']' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a,
1582+
"invalid syntax. Perhaps you meant '[]' instead of '[/]'?") }
1583+
1584+
invalid_tuple_slash:
1585+
| '(' a='/' ')' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a,
1586+
"invalid syntax. Perhaps you meant '()' instead of '(/)'?") }

Lib/_ast_unparse.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -772,9 +772,7 @@ def visit_Set(self, node):
772772
with self.delimit("{", "}"):
773773
self.interleave(lambda: self.write(", "), self.traverse, node.elts)
774774
else:
775-
# `{}` would be interpreted as a dictionary literal, and
776-
# `set` might be shadowed. Thus:
777-
self.write('{*()}')
775+
self.write('{/}')
778776

779777
def visit_Dict(self, node):
780778
def write_key_value_pair(k, v):

Lib/reprlib.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def repr_array(self, x, level):
139139

140140
def repr_set(self, x, level):
141141
if not x:
142-
return 'set()'
142+
return '{/}'
143143
x = _possibly_sorted(x)
144144
return self._repr_iterable(x, level, '{', '}', self.maxset)
145145

Lib/test/test_ast/data/ast_repr.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ Module(body=[Expr(value=Lambda(args=arguments(...), body=Constant(...)))], type_
156156
Module(body=[Expr(value=Dict(keys=[Constant(...)], values=[Constant(...)]))], type_ignores=[])
157157
Module(body=[Expr(value=Dict(keys=[], values=[]))], type_ignores=[])
158158
Module(body=[Expr(value=Set(elts=[Constant(...)]))], type_ignores=[])
159+
Module(body=[Expr(value=Set(elts=[]))], type_ignores=[])
159160
Module(body=[Expr(value=Dict(keys=[Constant(...)], values=[Constant(...)]))], type_ignores=[])
160161
Module(body=[Expr(value=List(elts=[Constant(...), Constant(...)], ctx=Load(...)))], type_ignores=[])
161162
Module(body=[Expr(value=Tuple(elts=[Constant(...)], ctx=Load(...)))], type_ignores=[])

Lib/test/test_ast/snippets.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@
265265
"{}",
266266
# Set
267267
"{None,}",
268+
# Empty set
269+
"{/}",
268270
# Multiline dict (test for .lineno & .col_offset)
269271
"""{
270272
1
@@ -552,6 +554,7 @@ def main():
552554
('Expression', ('Dict', (1, 0, 1, 7), [('Constant', (1, 2, 1, 3), 1, None)], [('Constant', (1, 4, 1, 5), 2, None)])),
553555
('Expression', ('Dict', (1, 0, 1, 2), [], [])),
554556
('Expression', ('Set', (1, 0, 1, 7), [('Constant', (1, 1, 1, 5), None, None)])),
557+
('Expression', ('Set', (1, 0, 1, 3), [])),
555558
('Expression', ('Dict', (1, 0, 5, 6), [('Constant', (2, 6, 2, 7), 1, None)], [('Constant', (4, 10, 4, 11), 2, None)])),
556559
('Expression', ('List', (1, 0, 5, 6), [('Constant', (2, 6, 2, 7), 1, None), ('Constant', (4, 8, 4, 9), 1, None)], ('Load',))),
557560
('Expression', ('Tuple', (1, 0, 4, 6), [('Constant', (2, 6, 2, 7), 1, None)], ('Load',))),

0 commit comments

Comments
 (0)