Skip to content

Commit 65c657d

Browse files
committed
addressing sc feedback: reframe rationale section around syntactic consistency
1 parent bc1f1a5 commit 65c657d

File tree

1 file changed

+50
-45
lines changed

1 file changed

+50
-45
lines changed

peps/pep-0798.rst

Lines changed: 50 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -97,33 +97,38 @@ equivalent to ``(x async for ait in aits() for x in ait)``.
9797
Rationale
9898
=========
9999

100-
Combining iterable objects together into a single larger object is a common
101-
task. One `StackOverflow post
100+
Combining multiple iterable objects together into a single object is a common
101+
task. For example, one `StackOverflow post
102102
<https://stackoverflow.com/questions/952914/how-do-i-make-a-flat-list-out-of-a-list-of-lists>`_
103-
asking about flattening a list of lists, for example, has been viewed 4.6
104-
million times. Despite this being a common operation, the options currently
105-
available for performing it concisely require levels of indirection that can
106-
make the resulting code difficult to read and understand.
107-
108-
The proposed notation is concise (avoiding the use and repetition of auxiliary
109-
variables) and, we expect, intuitive and familiar to programmers familiar with
110-
both comprehensions and unpacking notation (see :ref:`pep798-examples` for
111-
examples of code from the standard library that could be rewritten more clearly
112-
and concisely using the proposed syntax).
113-
114-
This proposal was motivated in part by a written exam in a Python programming
115-
class, where several students used the notation (specifically the ``set``
116-
version) in their solutions, assuming that it already existed in Python. This
117-
suggests that the notation is intuitive, even to beginners. By contrast, the
118-
existing syntax ``[x for it in its for x in it]`` is one that students often
119-
get wrong, the natural impulse for many students being to reverse the order of
120-
the ``for`` clauses.
121-
122-
Additionally, the comment section of a `Reddit post
103+
asking about flattening a list of lists has been viewed 4.6 million times, and
104+
there are several examples of code from the standard library that perform this
105+
operation (see :ref:`pep798-examples`). While Python provides a means of
106+
combining a small, known number of iterables using extended unpacking from
107+
:pep:`448`, no comparable syntax currently exists for combining an arbitrary
108+
number of iterables.
109+
110+
This proposal represents a natural extension of the language, paralleling
111+
existing syntactic structures: where ``[x, y, z]`` creates a list from a fixed
112+
number of vaues, ``[item for item in items]`` creates a list from an arbitrary
113+
number of values; this proposal extends that notion to the construction of
114+
lists that involve unpacking, making ``[*item for item in items]`` analogous to
115+
``[*x, *y, *z]``.
116+
117+
We expect this syntax to be intuitive and familiar to programmers already
118+
comfortable with both comprehensions and unpacking notation. This proposal was
119+
motivated in part by a written exam in a Python programming class, where
120+
several students used the proposed notation (specifically the ``set`` version)
121+
in their solutions, assuming that it already existed in Python. This suggests
122+
that the notation represents a logical, consistent extension to Python's
123+
existing syntax. By contrast, the existing double-loop version ``[x for it in
124+
its for x in it]`` is one that students often get wrong, the natural impulse
125+
for many students being to reverse the order of the ``for`` clauses. The
126+
intuitiveness of the proposed syntax is further supported by the comment
127+
section of a `Reddit post
123128
<https://old.reddit.com/r/Python/comments/1m607oi/pep_798_unpacking_in_comprehensions/>`__
124-
following the publication of this PEP shows substantial support for the
125-
proposal and further suggests that the syntax proposed here is legible,
126-
intuitive, and useful.
129+
made following the initial publication of this PEP, which demonstrates support
130+
from a broader community.
131+
127132

128133
Specification
129134
=============
@@ -412,9 +417,9 @@ Code Examples
412417
=============
413418

414419
This section shows some illustrative examples of how small pieces of code from
415-
the standard library could be rewritten to make use of this new syntax to
416-
improve concision and readability. The :ref:`pep798-reference` continues to
417-
pass all tests with these replacements made.
420+
the standard library could be rewritten to make use of this new syntax. The
421+
:ref:`pep798-reference` continues to pass all tests with these replacements
422+
made.
418423

419424
Replacing Explicit Loops
420425
------------------------
@@ -430,7 +435,7 @@ need for defining and referencing an auxiliary variable.
430435
comments.extend(token.comments)
431436
return comments
432437

433-
# improved:
438+
# proposed:
434439
return [*token.comments for token in self]
435440

436441
* From ``shutil.py``::
@@ -441,7 +446,7 @@ need for defining and referencing an auxiliary variable.
441446
ignored_names.extend(fnmatch.filter(names, pattern))
442447
return set(ignored_names)
443448

444-
# improved:
449+
# proposed:
445450
return {*fnmatch.filter(names, pattern) for pattern in patterns}
446451

447452
* From ``http/cookiejar.py``::
@@ -452,7 +457,7 @@ need for defining and referencing an auxiliary variable.
452457
cookies.extend(self._cookies_for_domain(domain, request))
453458
return cookies
454459

455-
# improved:
460+
# proposed:
456461
return [
457462
*self._cookies_for_domain(domain, request)
458463
for domain in self._cookies.keys()
@@ -463,8 +468,8 @@ Replacing from_iterable and Friends
463468

464469
While not always the right choice, replacing ``itertools.chain.from_iterable``
465470
and ``map`` can avoid an extra level of redirection, resulting in code that
466-
follows conventional wisdom that comprehensions are more readable than
467-
map/filter.
471+
follows conventional wisdom that comprehensions are generally more readable
472+
than map/filter.
468473

469474
* From ``dataclasses.py``::
470475

@@ -473,7 +478,7 @@ map/filter.
473478
itertools.chain.from_iterable(map(_get_slots, cls.__mro__[1:-1]))
474479
)
475480

476-
# improved:
481+
# proposed:
477482
inherited_slots = {*_get_slots(c) for c in cls.__mro__[1:-1]}
478483

479484
* From ``importlib/metadata/__init__.py``::
@@ -483,23 +488,23 @@ map/filter.
483488
path.search(prepared) for path in map(FastPath, paths)
484489
)
485490

486-
# improved:
491+
# proposed:
487492
return (*FastPath(path).search(prepared) for path in paths)
488493

489494
* From ``collections/__init__.py`` (``Counter`` class)::
490495

491496
# current:
492497
return _chain.from_iterable(_starmap(_repeat, self.items()))
493498

494-
# improved:
499+
# proposed:
495500
return (*_repeat(elt, num) for elt, num in self.items())
496501

497502
* From ``zipfile/_path/__init__.py``::
498503

499504
# current:
500505
parents = itertools.chain.from_iterable(map(_parents, names))
501506

502-
# improved:
507+
# proposed:
503508
parents = (*_parents(name) for name in names)
504509

505510
* From ``_pyrepl/_module_completer.py``::
@@ -510,7 +515,7 @@ map/filter.
510515
for spec in specs if spec
511516
))
512517

513-
# improved:
518+
# proposed:
514519
search_locations = {
515520
*getattr(spec, 'submodule_search_locations', [])
516521
for spec in specs if spec
@@ -520,30 +525,30 @@ Replacing Double Loops in Comprehensions
520525
----------------------------------------
521526

522527
Replacing double loops in comprehensions avoids the need for defining and
523-
referencing an auxiliary variable, reducing clutter.
528+
referencing an auxiliary variable.
524529

525530
* From ``importlib/resources/readers.py``::
526531

527532
# current:
528533
children = (child for path in self._paths for child in path.iterdir())
529534

530-
# improved:
535+
# proposed:
531536
children = (*path.iterdir() for path in self._paths)
532537

533538
* From ``asyncio/base_events.py``::
534539

535540
# current:
536541
exceptions = [exc for sub in exceptions for exc in sub]
537542

538-
# improved:
543+
# proposed:
539544
exceptions = [*sub for sub in exceptions]
540545

541546
* From ``_weakrefset.py``::
542547

543548
# current:
544549
return self.__class__(e for s in (self, other) for e in s)
545550

546-
# improved:
551+
# proposed:
547552
return self.__class__(*s for s in (self, other))
548553

549554

@@ -817,8 +822,8 @@ this syntax was clear and intuitive, several concerns and potential downsides
817822
were raised as well. This section aims to summarize those concerns.
818823

819824
* **Overlap with existing alternatives:**
820-
While the proposed syntax is arguably clearer and more concise, there are
821-
already several ways to accomplish this same thing in Python.
825+
While the proposed syntax is arguably more concise, there are already several
826+
ways to accomplish this same thing in Python.
822827

823828
* **Function call ambiguity:**
824829
Expressions like ``f(*x for x in y)`` may initially appear ambiguous, as it's
@@ -828,7 +833,7 @@ were raised as well. This section aims to summarize those concerns.
828833
may not be immediately obvious.
829834

830835
* **Potential for overuse or abuse:**
831-
Complex uses of unpacking in comprehensions could obscure logic that would be
836+
Complex uses of unpacking in comprehensions could obscure logic that may be
832837
clearer in an explicit loop. While this is already a concern with
833838
comprehensions more generally, the addition of ``*`` and ``**`` may make
834839
particularly complex uses even more difficult to read and understand at a

0 commit comments

Comments
 (0)