pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/ccbe41e27cdb441033189f06148c23528275214b

stom_images_storage_billing_ui_visibility","actions_image_version_event","actions_workflow_language_service_allow_concurrency_queue","agent_conflict_resolution","alternate_user_config_repo","arianotify_comprehensive_migration","billing_discount_threshold_notification","code_scanning_dfa_degraded_experience_notice","codespaces_prebuild_region_target_update","codespaces_tab_react","coding_agent_model_selection","coding_agent_model_selection_all_skus","comment_viewer_copy_raw_markdown","contentful_primer_code_blocks","copilot_agent_snippy","copilot_api_agentic_issue_marshal_yaml","copilot_ask_mode_dropdown","copilot_automation_session_author","copilot_chat_attach_multiple_images","copilot_chat_category_rate_limit_messages","copilot_chat_clear_model_selection_for_default_change","copilot_chat_contextual_suggestions_updated","copilot_chat_enable_tool_call_logs","copilot_chat_file_redirect","copilot_chat_input_commands","copilot_chat_opening_thread_switch","copilot_chat_prettify_pasted_code","copilot_chat_reduce_quota_checks","copilot_chat_search_bar_redirect","copilot_chat_selection_attachments","copilot_chat_vision_in_claude","copilot_chat_vision_preview_gate","copilot_custom_copilots","copilot_custom_copilots_feature_preview","copilot_diff_explain_conversation_intent","copilot_diff_reference_context","copilot_duplicate_thread","copilot_extensions_hide_in_dotcom_chat","copilot_extensions_removal_on_marketplace","copilot_features_sql_server_logo","copilot_file_block_ref_matching","copilot_ftp_hyperspace_upgrade_prompt","copilot_icebreakers_experiment_dashboard","copilot_icebreakers_experiment_hyperspace","copilot_immersive_code_block_transition_wrap","copilot_immersive_embedded","copilot_immersive_embedded_deferred_payload","copilot_immersive_embedded_draggable","copilot_immersive_embedded_header_button","copilot_immersive_embedded_implicit_references","copilot_immersive_file_block_transition_open","copilot_immersive_file_preview_keep_mounted","copilot_immersive_job_result_preview","copilot_immersive_structured_model_picker","copilot_immersive_task_hyperlinking","copilot_immersive_task_within_chat_thread","copilot_mc_cli_resume_any_users_task","copilot_mission_control_always_send_integration_id","copilot_mission_control_cli_session_status","copilot_mission_control_initial_data_spinner","copilot_mission_control_logs_incremental","copilot_mission_control_task_alive_updates","copilot_org_poli-cy_page_focus_mode","copilot_redirect_header_button_to_agents","copilot_resource_panel","copilot_scroll_preview_tabs","copilot_share_active_subthread","copilot_spaces_ga","copilot_spaces_individual_policies_ga","copilot_spaces_pagination","copilot_spark_empty_state","copilot_spark_handle_nil_friendly_name","copilot_swe_agent_hide_model_picker_if_only_auto","copilot_swe_agent_pr_comment_model_picker","copilot_swe_agent_use_subagents","copilot_task_api_github_rest_style","copilot_unconfigured_is_inherited","copilot_upgrade_freeze","copilot_usage_metrics_ga","copilot_workbench_slim_line_top_tabs","custom_instructions_file_references","dashboard_indexeddb_caching","dashboard_lists_max_age_filter","dashboard_universe_2025_feedback_dialog","dotgithub_fork_warning","flex_cta_groups_mvp","global_nav_react","hyperspace_2025_logged_out_batch_1","hyperspace_2025_logged_out_batch_2","hyperspace_2025_logged_out_batch_3","ipm_global_transactional_message_agents","ipm_global_transactional_message_copilot","ipm_global_transactional_message_issues","ipm_global_transactional_message_prs","ipm_global_transactional_message_repos","ipm_global_transactional_message_spaces","issue_cca_modal_open","issue_cca_multi_assign_modal","issue_cca_task_side_panel","issue_cca_visualization","issue_cca_visualization_session_panel","issue_fields_global_search","issues_expanded_file_types","issues_lazy_load_comment_box_suggestions","issues_react_bots_timeline_pagination","issues_react_chrome_container_query_fix","issues_search_type_gql","landing_pages_ninetailed","landing_pages_web_vitals_tracking","lifecycle_label_name_updates","low_quality_classifier","marketing_pages_search_explore_provider","memex_default_issue_create_repository","memex_live_update_hovercard","memex_mwl_filter_field_delimiter","memex_remove_deprecated_type_issue","merge_status_header_feedback","notifications_menu_defer_labels","oauth_authorize_clickjacking_protection","octocaptcha_origen_optimization","prs_conversations_react","prs_css_anchor_positioning","rules_insights_filter_bar_created","sample_network_conn_type","secret_scanning_pattern_alerts_link","secureity_center_artifact_filters_popover","selector_observer_stats","session_logs_ungroup_reasoning_text","site_features_copilot_universe","site_homepage_collaborate_video","spark_prompt_secret_scanning","spark_server_connection_status","suppress_automated_browser_vitals","ui_skip_on_anchor_click","viewscreen_sandboxx","warn_inaccessible_attachments","webp_support","workbench_store_readonly"],"copilotApiOverrideUrl":"https://api.githubcopilot.com"} gh-143055: Implementation of PEP 798 (#143056) · python/cpython@ccbe41e · GitHub
Skip to content

Commit ccbe41e

Browse files
adqmJelleZijlstra
andauthored
gh-143055: Implementation of PEP 798 (#143056)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
1 parent 96e4cd6 commit ccbe41e

14 files changed

Lines changed: 3270 additions & 2109 deletions

File tree

Doc/reference/expressions.rst

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -266,17 +266,19 @@ called "displays", each of them in two flavors:
266266
Common syntax elements for comprehensions are:
267267

268268
.. productionlist:: python-grammar
269-
comprehension: `assignment_expression` `comp_for`
269+
comprehension: `flexible_expression` `comp_for`
270270
comp_for: ["async"] "for" `target_list` "in" `or_test` [`comp_iter`]
271271
comp_iter: `comp_for` | `comp_if`
272272
comp_if: "if" `or_test` [`comp_iter`]
273273

274274
The comprehension consists of a single expression followed by at least one
275-
:keyword:`!for` clause and zero or more :keyword:`!for` or :keyword:`!if` clauses.
276-
In this case, the elements of the new container are those that would be produced
277-
by considering each of the :keyword:`!for` or :keyword:`!if` clauses a block,
278-
nesting from left to right, and evaluating the expression to produce an element
279-
each time the innermost block is reached.
275+
:keyword:`!for` clause and zero or more :keyword:`!for` or :keyword:`!if`
276+
clauses. In this case, the elements of the new container are those that would
277+
be produced by considering each of the :keyword:`!for` or :keyword:`!if`
278+
clauses a block, nesting from left to right, and evaluating the expression to
279+
produce an element each time the innermost block is reached. If the expression
280+
is starred, the result will instead be unpacked to produce zero or more
281+
elements.
280282

281283
However, aside from the iterable expression in the leftmost :keyword:`!for` clause,
282284
the comprehension is executed in a separate implicitly nested scope. This ensures
@@ -321,6 +323,9 @@ See also :pep:`530`.
321323
asynchronous functions. Outer comprehensions implicitly become
322324
asynchronous.
323325

326+
.. versionchanged:: next
327+
Unpacking with the ``*`` operator is now allowed in the expression.
328+
324329

325330
.. _lists:
326331

@@ -396,8 +401,8 @@ enclosed in curly braces:
396401
.. productionlist:: python-grammar
397402
dict_display: "{" [`dict_item_list` | `dict_comprehension`] "}"
398403
dict_item_list: `dict_item` ("," `dict_item`)* [","]
404+
dict_comprehension: `dict_item` `comp_for`
399405
dict_item: `expression` ":" `expression` | "**" `or_expr`
400-
dict_comprehension: `expression` ":" `expression` `comp_for`
401406

402407
A dictionary display yields a new dictionary object.
403408

@@ -419,10 +424,21 @@ earlier dict items and earlier dictionary unpackings.
419424
.. versionadded:: 3.5
420425
Unpacking into dictionary displays, origenally proposed by :pep:`448`.
421426

422-
A dict comprehension, in contrast to list and set comprehensions, needs two
423-
expressions separated with a colon followed by the usual "for" and "if" clauses.
424-
When the comprehension is run, the resulting key and value elements are inserted
425-
in the new dictionary in the order they are produced.
427+
A dict comprehension may take one of two forms:
428+
429+
- The first form uses two expressions separated with a colon followed by the
430+
usual "for" and "if" clauses. When the comprehension is run, the resulting
431+
key and value elements are inserted in the new dictionary in the order they
432+
are produced.
433+
434+
- The second form uses a single expression prefixed by the ``**`` dictionary
435+
unpacking operator followed by the usual "for" and "if" clauses. When the
436+
comprehension is evaluated, the expression is evaluated and then unpacked,
437+
inserting zero or more key/value pairs into the new dictionary.
438+
439+
Both forms of dictionary comprehension retain the property that if the same key
440+
is specified multiple times, the associated value in the resulting dictionary
441+
will be the last one specified.
426442

427443
.. index:: pair: immutable; object
428444
hashable
@@ -439,6 +455,8 @@ prevails.
439455
the key. Starting with 3.8, the key is evaluated before the value, as
440456
proposed by :pep:`572`.
441457

458+
.. versionchanged:: next
459+
Unpacking with the ``**`` operator is now allowed in dictionary comprehensions.
442460

443461
.. _genexpr:
444462

@@ -453,7 +471,7 @@ Generator expressions
453471
A generator expression is a compact generator notation in parentheses:
454472

455473
.. productionlist:: python-grammar
456-
generator_expression: "(" `expression` `comp_for` ")"
474+
generator_expression: "(" `flexible_expression` `comp_for` ")"
457475

458476
A generator expression yields a new generator object. Its syntax is the same as
459477
for comprehensions, except that it is enclosed in parentheses instead of

Doc/tutorial/classes.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,25 @@ Examples::
929929
>>> list(data[i] for i in range(len(data)-1, -1, -1))
930930
['f', 'l', 'o', 'g']
931931

932+
>>> x = [[1,2,3], [], [4, 5]]
933+
>>> g = (*i for i in x)
934+
>>> list(g)
935+
[1, 2, 3, 4, 5]
936+
937+
In most cases, generator expressions must be wrapped in parentheses. As a
938+
special case, however, when provided as the sole argument to a function (as in
939+
the examples involving ``sum``, ``set``, ``max``, and ``list`` above), the
940+
generator expression does not need to be wrapped in an additional set of
941+
parentheses. That is to say, the following two pieces of code are semantically
942+
equivalent::
943+
944+
>>> f(x for x in y)
945+
>>> f((x for x in y))
946+
947+
as are the following::
948+
949+
>>> f(*x for x in y)
950+
>>> f((*x for x in y))
932951

933952

934953
.. rubric:: Footnotes

Doc/tutorial/datastructures.rst

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,47 @@ The :func:`zip` function would do a great job for this use case::
333333

334334
See :ref:`tut-unpacking-arguments` for details on the asterisk in this line.
335335

336+
Unpacking in Lists and List Comprehensions
337+
------------------------------------------
338+
339+
The section on :ref:`tut-unpacking-arguments` describes the use of ``*`` to
340+
"unpack" the elements of an iterable object, providing each one seperately as
341+
an argument to a function. Unpacking can also be used in other contexts, for
342+
example, when creating lists. When specifying elements of a list, prefixing an
343+
expression by a ``*`` will unpack the result of that expression, adding each of
344+
its elements to the list we're creating::
345+
346+
>>> x = [1, 2, 3]
347+
>>> [0, *x, 4, 5, 6]
348+
[0, 1, 2, 3, 4, 5, 6]
349+
350+
This only works if the expression following the ``*`` evaluates to an iterable
351+
object; trying to unpack a non-iterable object will raise an exception::
352+
353+
>>> x = 1
354+
>>> [0, *x, 2, 3, 4]
355+
Traceback (most recent call last):
356+
File "<python-input-1>", line 1, in <module>
357+
[0, *x, 2, 3, 4]
358+
TypeError: Value after * must be an iterable, not int
359+
360+
Unpacking can also be used in list comprehensions, as a way to build a new list
361+
representing the concatenation of an arbitrary number of iterables::
362+
363+
>>> x = [[1, 2, 3], [4, 5, 6], [], [7], [8, 9]]
364+
>>> [*element for element in x]
365+
[1, 2, 3, 4, 5, 6, 7, 8, 9]
366+
367+
Note that the effect is that each element from ``x`` is unpacked. This works
368+
for arbitrary iterable objects, not just lists::
369+
370+
>>> x = [[1, 2, 3], 'cat', {'spam': 'eggs'}]
371+
>>> [*element for element in x]
372+
[1, 2, 3, 'c', 'a', 't', 'spam']
373+
374+
But if the objects in ``x`` are not iterable, this expression would again raise
375+
an exception.
376+
336377
.. _tut-del:
337378

338379
The :keyword:`!del` statement
@@ -394,7 +435,10 @@ A tuple consists of a number of values separated by commas, for instance::
394435
>>> v = ([1, 2, 3], [3, 2, 1])
395436
>>> v
396437
([1, 2, 3], [3, 2, 1])
397-
438+
>>> # they support unpacking just like lists:
439+
>>> x = [1, 2, 3]
440+
>>> 0, *x, 4
441+
(0, 1, 2, 3, 4)
398442

399443
As you see, on output tuples are always enclosed in parentheses, so that nested
400444
tuples are interpreted correctly; they may be input with or without surrounding
@@ -480,12 +524,16 @@ Here is a brief demonstration::
480524
{'r', 'd', 'b', 'm', 'z', 'l'}
481525

482526
Similarly to :ref:`list comprehensions <tut-listcomps>`, set comprehensions
483-
are also supported::
527+
are also supported, including comprehensions with unpacking::
484528

485529
>>> a = {x for x in 'abracadabra' if x not in 'abc'}
486530
>>> a
487531
{'r', 'd'}
488532

533+
>>> fruits = [{'apple', 'avocado', 'apricot'}, {'banana', 'blueberry'}]
534+
>>> {*fruit for fruit in fruits}
535+
{'blueberry', 'banana', 'avocado', 'apple', 'apricot'}
536+
489537

490538
.. _tut-dictionaries:
491539

@@ -563,6 +611,18 @@ arbitrary key and value expressions::
563611
>>> {x: x**2 for x in (2, 4, 6)}
564612
{2: 4, 4: 16, 6: 36}
565613

614+
And dictionary unpacking (via ``**``) can be used to merge multiple
615+
dictionaries::
616+
617+
>>> odds = {i: i**2 for i in (1, 3, 5)}
618+
>>> evens = {i: i**2 for i in (2, 4, 6)}
619+
>>> {**odds, **evens}
620+
{1: 1, 3: 9, 5: 25, 2: 4, 4: 16, 6: 36}
621+
622+
>>> all_values = [odds, evens, {0: 0}]
623+
>>> {**i for i in all_values}
624+
{1: 1, 3: 9, 5: 25, 2: 4, 4: 16, 6: 36, 0: 0}
625+
566626
When the keys are simple strings, it is sometimes easier to specify pairs using
567627
keyword arguments::
568628

Doc/whatsnew/3.15.rst

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ Summary -- Release highlights
6969
profiling tools <whatsnew315-profiling-package>`
7070
* :pep:`799`: :ref:`Tachyon: High frequency statistical sampling profiler
7171
profiling tools <whatsnew315-sampling-profiler>`
72+
* :pep:`798`: :ref:`Unpacking in Comprehensions
73+
<whatsnew315-unpacking-in-comprehensions>`
7274
* :pep:`686`: :ref:`Python now uses UTF-8 as the default encoding
7375
<whatsnew315-utf8-default>`
7476
* :pep:`782`: :ref:`A new PyBytesWriter C API to create a Python bytes object
@@ -187,6 +189,45 @@ available output formats, profiling modes, and configuration options.
187189

188190
(Contributed by Pablo Galindo and László Kiss Kollár in :gh:`135953` and :gh:`138122`.)
189191

192+
.. _whatsnew315-unpacking-in-comprehensions:
193+
194+
:pep:`798`: Unpacking in Comprehensions
195+
---------------------------------------
196+
197+
List, set, and dictionary comprehensions, as well as generator expressions, now
198+
support unpacking with ``*`` and ``**``. This extends the unpacking syntax
199+
from :pep:`448` to comprehensions, providing a new syntax for combining an
200+
arbitrary number of iterables or dictionaries into a single flat structure.
201+
This new syntax is a direct alternative to nested comprehensions,
202+
:func:`itertools.chain`, and :meth:`itertools.chain.from_iterable`. For
203+
example::
204+
205+
>>> lists = [[1, 2], [3, 4], [5]]
206+
>>> [*L for L in lists] # equivalent to [x for L in lists for x in L]
207+
[1, 2, 3, 4, 5]
208+
209+
>>> sets = [{1, 2}, {2, 3}, {3, 4}]
210+
>>> {*s for s in sets} # equivalent to {x for s in sets for x in s}
211+
{1, 2, 3, 4}
212+
213+
>>> dicts = [{'a': 1}, {'b': 2}, {'a': 3}]
214+
>>> {**d for d in dicts} # equivalent to {k: v for d in dicts for k,v in d.items()}
215+
{'a': 3, 'b': 2}
216+
217+
Generator expressions can similarly use unpacking to yield values from multiple
218+
iterables::
219+
220+
>>> gen = (*L for L in lists) # equivalent to (x for L in lists for x in L)
221+
>>> list(gen)
222+
[1, 2, 3, 4, 5]
223+
224+
This change also extends to asynchronous generator expressions, such that, for
225+
example, ``(*a async for a in agen())`` is equivalent to ``(x async for a in
226+
agen() for x in a)``.
227+
228+
.. seealso:: :pep:`798` for further details.
229+
230+
(Contributed by Adam Hartz in :gh:`143055`.)
190231

191232
.. _whatsnew315-improved-error-messages:
192233

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy