diff --git a/pep-0572.rst b/pep-0572.rst index bfaa7fb59f9..e679edb8260 100644 --- a/pep-0572.rst +++ b/pep-0572.rst @@ -12,18 +12,23 @@ Post-History: 28-Feb-2018, 02-Mar-2018, 23-Mar-2018 Abstract ======== -Programming is all about reusing code rather than duplicating it. When -an expression needs to be used twice in quick succession but never again, -it is convenient to assign it to a temporary name with small scope. -By permitting name bindings to exist within a single statement only, we -make this both convenient and safe against name collisions. +This is a proposal for permitting temporary name bindings +which are limited to a single statement. Rationale ========= -When a subexpression is used multiple times in a list comprehension, there -are currently several ways to spell this, none of which is universally +Programmers generally prefer reusing code rather than duplicating it. When +an expression needs to be used twice in quick succession but never again, +it is convenient to assign it to a temporary name with small scope. +By permitting name bindings to exist within a single statement only, we +make this both convenient and safe against name collisions. + +This is particularly notable in list/dict/set comprehensions and generator +expressions, where refactoring a subexpression into an assignment statement +is not possible. There are currently several ways to create a temporary name +binding inside a list comprehension, none of which is universally accepted as ideal. A statement-local name allows any subexpression to be temporarily captured and then used multiple times. @@ -43,7 +48,13 @@ and ``NAME`` is a simple name. The value of such a named expression is the same as the incorporated expression, with the additional side-effect that NAME is bound to that -value for the remainder of the current statement. +value for the remainder of the current statement. For example:: + + # Similar to the boolean 'or' but checking for None specifically + x = "default" if (spam().ham as eggs) is None else eggs + + # Even complex expressions can be built up piece by piece + y = ((spam() as eggs), (eggs.method() as cheese), cheese[eggs]) Just as function-local names shadow global names for the scope of the function, statement-local names shadow other names for that statement. @@ -252,14 +263,14 @@ Both of these are forbidden; creating SLNBs in the headers of these statements will result in a SyntaxError. -Alternative proposals -===================== +Alternative proposals and variants +================================== Proposals broadly similar to this one have come up frequently on python-ideas. Below are a number of alternative syntaxes, some of them specific to comprehensions, which have been rejected in favour of the one given above. -1. ``where``, ``let``, ``given``:: +1. ``where``, ``let``, or ``given``, in comprehensions only:: stuff = [(y, x/y) where y = f(x) for x in range(5)] stuff = [(y, x/y) let y = f(x) for x in range(5)] @@ -337,6 +348,15 @@ comprehensions, which have been rejected in favour of the one given above. ``f(x) < 0`` and you want to capture the value of ``f(x)``). It also has no benefit to list comprehensions. +8. Adding a ``where:`` to any statement to create local name bindings:: + + value = x**2 + 2*x where: + x = spam(1, 4, 7, q) + + Execution order is inverted (the indented body is performed first, followed + by the "header"). This requires a new keyword, unless an existing keyword + is repurposed (most likely ``with:``). + Discrepancies in the current implementation ===========================================