@@ -97,33 +97,38 @@ equivalent to ``(x async for ait in aits() for x in ait)``.
9797Rationale
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
128133Specification
129134=============
@@ -412,9 +417,9 @@ Code Examples
412417=============
413418
414419This 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
419424Replacing 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
464469While not always the right choice, replacing ``itertools.chain.from_iterable ``
465470and ``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
522527Replacing 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
@@ -773,9 +778,9 @@ Beyond the proposal outlined above, the following were also considered:
773778 This strategy would also make unpacking in synchronous and asynchronous
774779 generators behave symmetrically, but it would also be more complex, enough
775780 so that the cost may not be worth the benefit. As such, this PEP proposes
776- that generator expressions using the unpacking operator should not use
777- semantics similar to ``yield from `` until ``yield from `` is supported in
778- asynchronous generators more generally.
781+ that asynchronous generator expressions using the unpacking operator should
782+ not adopt semantics similar to ``yield from `` until ``yield from `` is
783+ supported in asynchronous generators more generally.
779784
7807853. Using ``yield from `` for unpacking in synchronous generator expressions, and
781786 disallowing unpacking in asynchronous generator expressions until they
@@ -817,8 +822,9 @@ this syntax was clear and intuitive, several concerns and potential downsides
817822were 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 represents a consistent extension to the language
826+ and is likely to result in more-concise code, there are already several ways
827+ to accomplish this same thing in Python.
822828
823829* **Function call ambiguity: **
824830 Expressions like ``f(*x for x in y) `` may initially appear ambiguous, as it's
@@ -828,13 +834,13 @@ were raised as well. This section aims to summarize those concerns.
828834 may not be immediately obvious.
829835
830836* **Potential for overuse or abuse: **
831- Complex uses of unpacking in comprehensions could obscure logic that would be
837+ Complex uses of unpacking in comprehensions could obscure logic that may be
832838 clearer in an explicit loop. While this is already a concern with
833839 comprehensions more generally, the addition of ``* `` and ``** `` may make
834840 particularly complex uses even more difficult to read and understand at a
835841 glance. For example, while these situations are likely rare, comprehensions
836842 that use unpacking in multiple ways can make it difficult to know what's
837- being unpacked and when: ``f(*(*x for *x, _ in list_of_lists)) ``.
843+ being unpacked and when, e.g., ``f(*(*x for *x, _ in list_of_lists)) ``.
838844
839845* **Unclear limitation of scope: **
840846 This proposal restricts unpacking to the top level of the comprehension
0 commit comments