I have been struggling to justify the need based on what I have read. I hope this isn't a dupe, I only saw caching mentioned in passing.
Also please excuse some of the naive generalizations below for illustrative purposes. Is there a reason memoization doesn't work? If f is truly expensive, using this syntax only makes sense if you have few comprehensions (as opposed to many iterations) and few other calls to f. Calling f in 10 comprehensions would (naively) benefit from memoization more than this. It appears to me to be ad-hoc memoization with limited scope. is this a fair statement? >From readability, the examples put forth have been to explain the advantage, with which I agree. However, i do not believe this scales well. [(foo(x,y) as g)*(bar(y) as i) + g*foo(x,a) +baz(g,i) for x... for y...] That's 3 functions, 2 iterators, 3 calls saved ('a' is some constant just to trigger a new call on foo). I'm not trying to show ugly statements can be constructed, but show how quickly in _n iterators and _m functions readability declines. it's seems the utility is bounded by f being not complex/costly enough for memoization, and ( _m, _n) being small enough to pass code review. The syntax does not create a readability issue, but by adding symbols, exacerbates an inherent issue with 'complex' comprehensions. If I have understood, does this bounding on f, _m, and _n yield a tool with sufficient applicability for a language change? I know it's already spoken about as an edge case, I'm just not clear on the bounds of that. I am not against it, I just haven't seen this addressed. Thank you for putting the PEP together. Again, I don't want to sound negative on it, I may have misunderstood wildly. I like the idea conceptually, and I don't think it's anymore confusing to me than comprehensions were when I first encountered them, and it will yield a 'cool!' statement much like they did when I learned them. On Tue, Feb 27, 2018, 22:55 Gregory P. Smith <g...@krypto.org> wrote: > > On Tue, Feb 27, 2018 at 2:35 PM Chris Angelico <ros...@gmail.com> wrote: > >> This is a suggestion that comes up periodically here or on python-dev. >> This proposal introduces a way to bind a temporary name to the value >> of an expression, which can then be used elsewhere in the current >> statement. >> >> The nicely-rendered version will be visible here shortly: >> >> https://www.python.org/dev/peps/pep-0572/ >> >> ChrisA >> >> PEP: 572 >> Title: Syntax for Statement-Local Name Bindings >> Author: Chris Angelico <ros...@gmail.com> >> Status: Draft >> Type: Standards Track >> Content-Type: text/x-rst >> Created: 28-Feb-2018 >> Python-Version: 3.8 >> Post-History: 28-Feb-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 very small scope. >> By permitting name bindings to exist within a single statement only, we >> make this both convenient and safe against collisions. >> >> >> Rationale >> ========= >> >> When an expression is used multiple times in a list comprehension, there >> are currently several suboptimal ways to spell this, and no truly good >> ways. A statement-local name allows any expression to be temporarily >> captured and then used multiple times. >> >> >> Syntax and semantics >> ==================== >> >> In any context where arbitrary Python expressions can be used, a named >> expression can appear. This must be parenthesized for clarity, and is of >> the form `(expr as NAME)` where `expr` is any valid Python expression, >> 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. >> >> Just as function-local names shadow global names for the scope of the >> function, statement-local names shadow other names for that statement. >> They can also shadow each other, though actually doing this should be >> strongly discouraged in style guides. >> >> >> Example usage >> ============= >> >> These list comprehensions are all approximately equivalent:: >> >> # Calling the function twice >> stuff = [[f(x), f(x)] for x in range(5)] >> >> # Helper function >> def pair(value): return [value, value] >> stuff = [pair(f(x)) for x in range(5)] >> >> # Inline helper function >> stuff = [(lambda v: [v,v])(f(x)) for x in range(5)] >> >> # Extra 'for' loop - see also Serhiy's optimization >> stuff = [[y, y] for x in range(5) for y in [f(x)]] >> >> # Expanding the comprehension into a loop >> stuff = [] >> for x in range(5): >> y = f(x) >> stuff.append([y, y]) >> >> # Using a statement-local name >> stuff = [[(f(x) as y), y] for x in range(5)] >> >> If calling `f(x)` is expensive or has side effects, the clean operation of >> the list comprehension gets muddled. Using a short-duration name binding >> retains the simplicity; while the extra `for` loop does achieve this, it >> does so at the cost of dividing the expression visually, putting the named >> part at the end of the comprehension instead of the beginning. >> >> Statement-local name bindings can be used in any context, but should be >> avoided where regular assignment can be used, just as `lambda` should be >> avoided when `def` is an option. >> >> >> Open questions >> ============== >> >> 1. What happens if the name has already been used? `(x, (1 as x), x)` >> Currently, prior usage functions as if the named expression did not >> exist (following the usual lookup rules); the new name binding will >> shadow the other name from the point where it is evaluated until the >> end of the statement. Is this acceptable? Should it raise a syntax >> error or warning? >> >> 2. The current implementation [1] implements statement-local names using >> a special (and mostly-invisible) name mangling. This works perfectly >> inside functions (including list comprehensions), but not at top >> level. Is this a serious limitation? Is it confusing? >> >> 3. The interaction with locals() is currently[1] slightly buggy. Should >> statement-local names appear in locals() while they are active (and >> shadow any other names from the same function), or should they simply >> not appear? >> >> 4. Syntactic confusion in `except` statements. While technically >> unambiguous, it is potentially confusing to humans. In Python 3.7, >> parenthesizing `except (Exception as e):` is illegal, and there is no >> reason to capture the exception type (as opposed to the exception >> instance, as is done by the regular syntax). Should this be made >> outright illegal, to prevent confusion? Can it be left to linters? >> >> 5. Similar confusion in `with` statements, with the difference that there >> is good reason to capture the result of an expression, and it is also >> very common for `__enter__` methods to return `self`. In many cases, >> `with expr as name:` will do the same thing as `with (expr as name):`, >> adding to the confusion. >> >> >> References >> ========== >> >> .. [1] Proof of concept / reference implementation >> (https://github.com/Rosuav/cpython/tree/statement-local-variables) >> >> > -1 today > > My first concern for this proposal is that it gives parenthesis a > non-intuitive meaning. ()s used to be innocuous. A way to group things, > make explicit the desired order of operations, and allow spanning across > multiple lines. > > With this proposal, ()s occasionally take on a new meaning to cause > additional action to happen _outside_ of the ()s - an expression length > name binding. > > Does this parse? > > print(fetch_the_comfy_chair(cardinal) as chair, "==", chair) > > SyntaxError? My read of the proposal suggests that this would be required: > > print((fetch_the_comfy_chair(cardinal) as chair), "==", chair) > > But that sets off my "excess unnecessary parenthesis" radar as this is a > new need it isn't trained for. I could retrain the radar... > > I see people try to cram too much functionality into one liners. By the > time you need to refer to a side effect value more than once in a single > statement... Just use multiple statements. It is more clear and easier to > debug. > > Anything else warps human minds trying to read, understand, review, and > maintain the code. {see_also: "nested list comprehensions"} > > '''insert favorite Zen of Python quotes here''' > > We've got a whitespace delimited language. That is a feature. Lets keep it > that way rather than adding a form of (non-curly) braces. > > I do understand the desire. So far I remain unconvinced of a need. > > Practical examples from existing code that become clearly easier to > understand afterwards instead of made the examples with one letter names > may help. > > 2cents, > -gps > > _______________________________________________ > Python-ideas mailing list > Python-ideas@python.org > https://mail.python.org/mailman/listinfo/python-ideas > Code of Conduct: http://python.org/psf/codeofconduct/ >
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/