Great work Chris! Thank you! I do not know whether this is good or bad, but this PEP considers so many different topics, although closely interrelated with each other.
2018-04-11 8:32 GMT+03:00 Chris Angelico <ros...@gmail.com>: > > Alterations to comprehensions > ----------------------------- > > The current behaviour of list/set/dict comprehensions and generator > expressions has some edge cases that would behave strangely if an > assignment > expression were to be used. Therefore the proposed semantics are changed, > removing the current edge cases, and instead altering their behaviour > *only* > in a class scope. > > As of Python 3.7, the outermost iterable of any comprehension is evaluated > in the surrounding context, and then passed as an argument to the implicit > function that evaluates the comprehension. > > Under this proposal, the entire body of the comprehension is evaluated in > its implicit function. Names not assigned to within the comprehension are > located in the surrounding scopes, as with normal lookups. As one special > case, a comprehension at class scope will **eagerly bind** any name which > is already defined in the class scope. > > I think this change is important one no matter what will be the future of the current PEP. And since it breaks backward compatibility it deserves a separate PEP. > Open questions > ============== > > Can the outermost iterable still be evaluated early? > ---------------------------------------------------- > > As of Python 3.7, the outermost iterable in a genexp is evaluated early, > and > the result passed to the implicit function as an argument. With PEP 572, > this > would no longer be the case. Can we still, somehow, evaluate it before > moving > on? One possible implementation would be:: > > gen = (x for x in rage(10)) > # translates to > def <genexp>(): > iterable = iter(rage(10)) > yield None > for x in iterable: > yield x > gen = <genexp>() > next(gen) > > This would pump the iterable up to just before the loop starts, evaluating > exactly as much as is evaluated outside the generator function in Py3.7. > This would result in it being possible to call ``gen.send()`` immediately, > unlike with most generators, and may incur unnecessary overhead in the > common case where the iterable is pumped immediately (perhaps as part of a > larger expression). > > Previously, there was an alternative _operator form_ `->` proposed by Steven D'Aprano. This option is no longer considered? I see several advantages with this variant: 1. It does not use `:` symbol which is very visually overloaded in Python. 2. It is clearly distinguishable from the usual assignment statement and it's `+=` friends There are others but they are minor. > Frequently Raised Objections > ============================ > > Why not just turn existing assignment into an expression? > --------------------------------------------------------- > > C and its derivatives define the ``=`` operator as an expression, rather > than > a statement as is Python's way. This allows assignments in more contexts, > including contexts where comparisons are more common. The syntactic > similarity > between ``if (x == y)`` and ``if (x = y)`` belies their drastically > different > semantics. Thus this proposal uses ``:=`` to clarify the distinction. > > > This could be used to create ugly code! > --------------------------------------- > > So can anything else. This is a tool, and it is up to the programmer to > use it > where it makes sense, and not use it where superior constructs can be used. > > But the ugly code matters, especially when it comes to Python. For me, the ideal option would be the combination of two rejected parts: Special-casing conditional statements > ------------------------------------- > > One of the most popular use-cases is ``if`` and ``while`` statements. > Instead > of a more general solution, this proposal enhances the syntax of these two > statements to add a means of capturing the compared value:: > > if re.search(pat, text) as match: > print("Found:", match.group(0)) > > This works beautifully if and ONLY if the desired condition is based on the > truthiness of the captured value. It is thus effective for specific > use-cases (regex matches, socket reads that return `''` when done), and > completely useless in more complicated cases (eg where the condition is > ``f(x) < 0`` and you want to capture the value of ``f(x)``). It also has > no benefit to list comprehensions. > > Advantages: No syntactic ambiguities. Disadvantages: Answers only a > fraction > of possible use-cases, even in ``if``/``while`` statements. > (+ in `while`) combined with this part: 3. ``with EXPR as NAME``:: > > stuff = [(y, x/y) with f(x) as y for x in range(5)] > > As per option 2, but using ``as`` rather than an equals sign. Aligns > syntactically with other uses of ``as`` for name binding, but a simple > transformation to for-loop longhand would create drastically different > semantics; the meaning of ``with`` inside a comprehension would be > completely different from the meaning as a stand-alone statement, while > retaining identical syntax. > I see no benefit to have the assignment expression in other places. And all your provided examples use `while` or `if` or some form of comprehension. I also see no problem with `if (re.search(pat, text) as match) is not None:..`. What is the point of overloading language with expression that will be used only in `while` and `if` and will be rejected by style checkers in other places? With kind regards, -gdg
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/