<div dir="ltr">I like this idea in theory, but I'm not sold yet.<br><br>I think there's a lot of draw to the concept of "expressionizing" statements because many statements require an unnatural ordering in-which the most important code, the logic, comes after some necessary but ultimately noisy (from the readers perspective) preamble. So I expect people to keep asking for expressionized statements and slowly, but, surely, they'll make their way into the language. They just need to be very carefully thought out.<br><br>Expressionization may break the "one and only on obvious way" guideline, but it can offer concise, readable code in a lot of instances where a statement-based version would be clumsy and noisy, and there's already some precedent for it:<br><br>function declaration => lambda<br>for-loops => generator expressions and comprehensions<br>if-else => ternary statements<br><br>With the exception of lambda, expressionized statements usually allow one to put the "meat before the vegitables" so to speak. That is; the highest value part of the expression comes first and all the book-keeping follows. <span style="font-size:small;text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">To illustrate this, I'll write a part of an expression and gradually reveal the complete expression, you can see how progressively easier it is to predict the next reveal:</span><br><br><font face="monospace, monospace">def initials(people):</font><br><font face="monospace, monospace">    return {"".join(name[0] ...</font><br><br><font face="arial, helvetica, sans-serif"># The identifier "name" isn't in scope so it must be assigned in the for clause of a comprehension.</font><br><br><span style="font-family:monospace,monospace;font-size:small;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">def initials(people):</span><br style="font-size:small;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial"><span style="font-family:monospace,monospace;font-size:small;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">    return {"".join(name[0] for name in ...<br></span><br># This is a nested comprehension so it's not much of a surprise that the iterator might be related to another<br># yet-to-be assigned identifier.<br><br><span style="font-family:monospace,monospace;font-size:small;text-decoration-style:initial;text-decoration-color:initial;background-color:rgb(255,255,255);float:none;display:inline">def initials(people):</span><br style="font-size:small;text-decoration-style:initial;text-decoration-color:initial;background-color:rgb(255,255,255)"><span style="font-family:monospace,monospace;font-size:small;text-decoration-style:initial;text-decoration-color:initial;background-color:rgb(255,255,255);float:none;display:inline">    return {"".join(name[0] for name in person.names ...<br><br># Blindly accessing the first element of an empty sequence could cause problems<br></span><br><span style="font-family:monospace,monospace;font-size:small;text-decoration-style:initial;text-decoration-color:initial;background-color:rgb(255,255,255);float:none;display:inline">def initials(people):</span><br style="font-size:small;text-decoration-style:initial;text-decoration-color:initial;background-color:rgb(255,255,255)"><span style="font-family:monospace,monospace;font-size:small;text-decoration-style:initial;text-decoration-color:initial;background-color:rgb(255,255,255);float:none;display:inline">    return {"".join(name[0] for name in person.names if name) ...<br><br># The inner generator is closed but we still need a binding for "person"</span><span style="font-family:monospace,monospace;font-size:small;text-decoration-style:initial;text-decoration-color:initial;background-color:rgb(255,255,255);float:none;display:inline"><br></span><br><span style="font-family:monospace,monospace;font-size:small;text-decoration-style:initial;text-decoration-color:initial;background-color:rgb(255,255,255);float:none;display:inline">def initials(people):</span><br style="font-size:small;text-decoration-style:initial;text-decoration-color:initial;background-color:rgb(255,255,255)"><span style="font-family:monospace,monospace;font-size:small;text-decoration-style:initial;text-decoration-color:initial;background-color:rgb(255,255,255);float:none;display:inline">    return {"".join(name[0] for name in person.names if name) for person in ...<br></span><br># There's not much left to iterate over and decent variable names point to one obvious choice<br><div><font face="monospace, monospace"><br></font><div><span style="font-family:monospace,monospace;font-size:small;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">def initials(people):</span><br style="font-family:monospace,monospace;font-size:small;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial"><span style="font-family:monospace,monospace;font-size:small;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">    return {"".join(name[0] for name in person.names if name) for person in people}<br></span><br><br>The same could be said for lambdas if they were defined logic-first because they're usually used in a context where the call signature is obvious:<br><font face="monospace, monospace"><br>hand = sorted(cards, key=(card.suit if card is not wild else max_value <== card))[-5:]</font><br><br>Of course, no such thing exists so the '<==' syntax is made up (in-fact a possibly better alternative is, "with"), but it doesn't really matter because a reverse lambda isn't going to happen. You can see, however; that the function's signature is pretty obvious from context, so it's more for the computer's sake than the reader's sake and would be better placed out of the way.<br><br>I like the current proposal because it follows that design idea, however; I haven't taken the time to think about all the edge cases yet.<br>For instance, what would the following do?<br><br><font face="monospace, monospace">initial = <a href="http://person.name">person.name</a>[0] with suppress(AttributeError)  # Hangover from PEP 505 discussion...<br></font><br>Now that I think of it, this seems to inherently make assignment part of the expression:<br><font face="monospace, monospace"><br>data = file.read() with open(...) as file</font><br><br>is supposed to be equivalent to:<br><br><font face="monospace, monospace">with open(...) as file:<br>    data = file.read()</font><br><br>Right?<br><br>So maybe it only makes sense to use expression assignment (PEP 572):<br><br>data = (d := file.read() with open(...) as file)<br><br>To which I say, "Eww..."<br><br>Also:<br><br><font face="monospace, monospace">initial = (i := <a href="http://person.name">person.name</a>[0] with suppress(AttributeError))</font><br><br>Is still ambiguous (and still eww).<br><br>One tactic that other expressionizations have taken is to limit the scope. For instance, the ternary operation only covers expressionization of "if-else" not "just if" or "if-elif-..." or "if-elif-...-else", and generator expressions don't allow <a href="http://book.pythontips.com/en/latest/for_-_else.html">the 'else' clause</a> of normal for-loops. So maybe you can obviate some of the edge cases by requiring an as clause or something. I don't know how that would help with the suppress(AttributeError) case thought...<br></div></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Fri, Aug 3, 2018 at 12:56 PM, Todd <span dir="ltr"><<a href="mailto:toddrjen@gmail.com" target="_blank">toddrjen@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><div class="h5"><div class="gmail_extra"><div class="gmail_quote">On Thu, Aug 2, 2018 at 5:35 AM, Ken Hilton <span dir="ltr"><<a href="mailto:kenlhilton@gmail.com" target="_blank">kenlhilton@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">Hi, I don't know if someone has already suggested this before, but here goes:</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)"><br></div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">With expressions allow using the enter/exit semantics of the with statement inside an expression context. Examples:</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)"><br></div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">    contents = f.read() with open('file') as f #the most obvious one</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">    multiplecontents = [f.read() with open(name) as f for name in names] #reading multiple files</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)"><br></div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">I don't know if it's worth making the "as NAME" part of the with mandatory in an expression - is this a valid use case?</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)"><br></div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">    data = database.selectrows() with threadlock</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)"><br></div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">Where this would benefit: I think the major use case is `f.read() with open('file') as f`. Previous documentation has suggested `open('file').read()` and rely on garbage collection; as the disadvantages of that became obvious, it transitioned to a method that couldn't be done in an expression:</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)"><br></div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">    with open('file') as f:</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">        contents = f.read()</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)"><br></div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">Therefore `f.read() with open('file') as f`, I think, would be much welcomed as the best way to read a file in an expression.</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)"><br></div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">For those wondering about the scope semantics of the "as NAME", I think they would be identical to the scope semantics of the "for" expression - i.e. these are legal:</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)"><br></div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">    contents = f.read() with open('file') as f</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">    grid = [[i] * 4 for i in range(4)]</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)"><br></div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">But these are not:</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)"><br></div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">    contents = f.read() with open('file') as f</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">    f.seek(0)</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">    grid = [[i] * 4 for i in range(4)]</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">    grid[i][i] = 4</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)"><br></div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">Is this a good idea? Are there some subtleties I've failed to explain? Please let me know.</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)"><br></div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">Sharing,</div><div style="font-family:monospace,monospace;font-size:small;color:rgb(0,0,255)">Ken Hilton</div></div><br></blockquote></div></div><div class="gmail_extra"><br></div></div></div><div class="gmail_extra">If this is a common enough operation for you, it would be trivially easy to just write a function that does this.  There is already a module on pypi that has this function: read_and_close.<br></div></div>
<br>______________________________<wbr>_________________<br>
Python-ideas mailing list<br>
<a href="mailto:Python-ideas@python.org">Python-ideas@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-ideas" rel="noreferrer" target="_blank">https://mail.python.org/<wbr>mailman/listinfo/python-ideas</a><br>
Code of Conduct: <a href="http://python.org/psf/codeofconduct/" rel="noreferrer" target="_blank">http://python.org/psf/<wbr>codeofconduct/</a><br>
<br></blockquote></div><br></div>