Re: [Python-Dev] PEP 572: Write vs Read, Understand and Control Flow
Chris Angelico writes: > Additionally, naming sub-parts of a large expression can assist an > interactive debugger, providing useful display hooks and partial > results. Without a way to capture sub-expressions inline, this > would require refactoring of the original code; with assignment > expressions, this merely requires the insertion of a few ``name > :=`` markers. Removing the need to refactor reduces the likelihood > that the code be inadvertently changed as part of debugging (a > common cause of Heisenbugs), Period here preferred. > and is easier to dictate to a student or junior programmer. True but gratuitous. It's also true that it's easier to dictate to Guido or Tim, though you might be happier if you let them refactor! ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 572: Write vs Read, Understand and Control Flow
Devin Jeanpierre writes: > Some other programming languages (thinking of Racket) solve this by > having the debugger let you step through expression evaluation, > without editing the code. Good tools are a wonderful thing, and I think pdb should be enhanced that way (by somebody who has the time and interest, not me and not necessarily you ;-). Nevertheless, "printf debugging" continues to be very popular, and good support for printf debugging is indeed the killer app for binding expressions as far as I'm concerned. Tim's humorous insight took me from -0.8 all the way to +1 Nice job, Chris! Good luck with the pronouncement! Steve ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
> On Apr 26, 2018, at 12:40 AM, Tim Peterswrote: > > [Raymond Hettinger ] >> After re-reading all the proposed code samples, I believe that >> adopting the PEP will make the language harder to teach to people >> who are not already software engineers. > > Can you elaborate on that? Just distinguishing between =, :=, and == will be a forever recurring discussion, far more of a source of confusion than the occasional question of why Python doesn't have embedded assignment. Also, it is of concern that a number of prominent core dev respondents to this thread have reported difficulty scanning the posted code samples. > I've used dozens of languages over the > decades, most of which did have some form of embedded assignment. Python is special, in part, because it is not one of those languages. It has virtues that make it suitable even for elementary school children. We can show well-written Python code to non-computer folks and walk them through what it does without their brains melting (something I can't do with many of the other languages I've used). There is a virtue in encouraging simple statements that read like English sentences organized into English-like paragraphs, presenting itself like "executable pseudocode". Perl does it or C++ does it is unpersuasive. Its omission from Python was always something that I thought Guido had left-out on purpose, intentionally stepping away from constructs that would be of help in an obfuscated Python contest. > Yes, I'm a software engineer, but I've always pitched in on "help > forums" too. That's not really the same. I've taught Python to many thousands of professionals, almost every week for over six years. That's given me a keen sense of what is hard to teach. It's okay to not agree with my assessment, but I would like for fruits of my experience to not be dismissed in a single wisp of a sentence. Any one feature in isolation is usually easy to explain, but showing how to combine them into readable, expressive code is another matter. And as Yuri aptly noted, we spend more time reading code than writing code. If some fraction of our users finds the code harder to scan because the new syntax, then it would be a net loss for the language. I hesitated to join this thread because you and Guido seemed to be pushing back so hard against anyone's who design instincts didn't favor the new syntax. It would be nice to find some common ground and perhaps stipulate that the grammar would grow in complexity, that a new operator would add to the current zoo of operators, that the visual texture of the language would change (and in a way that some including me do not find pleasing), and that while simplest cases may afford a small net win, it is a certitude that the syntax will routinely be pushed beyond our comfort zone. While the regex conditional example looks like a win, it is very modest win and IMHO not worth the overall net increase language complexity. Like Yuri, I'll drop-out now. Hopefully, you all wind find some value in what I had to contribute to the conversation. Raymond ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
On Thu, Apr 26, 2018 at 3:34 PM, Steven D'Apranowrote: > On Thu, Apr 26, 2018 at 05:22:58PM +1200, Greg Ewing wrote: >> Łukasz Langa wrote: >> >What was its own assignment before >> >now is part of the logic test. This saves on vertical whitespace but makes >> >parsing and understanding logic tests harder. >> >> Another way to say this is that expressions are no longer >> restricted to being trees, but can be general DAGs, which >> require more mental effort to understand. > > Is that right? I presume you mean that there can be cycles in > expressions involving binding-expressions. If not, what do you mean? > > Can you give an example of a Python expression, involving PEP 572 > binding-expressions, that is not a tree but a more general DAG or that > contains cycles? A DAG is a directed *acyclic* graph, so it still can't contain cycles. But I have no idea what kind of expression isn't a tree as a consequence of having an assignment in it. ChrisA ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
On Thu, Apr 26, 2018 at 05:22:58PM +1200, Greg Ewing wrote: > Łukasz Langa wrote: > >What was its own assignment before > >now is part of the logic test. This saves on vertical whitespace but makes > >parsing and understanding logic tests harder. > > Another way to say this is that expressions are no longer > restricted to being trees, but can be general DAGs, which > require more mental effort to understand. Is that right? I presume you mean that there can be cycles in expressions involving binding-expressions. If not, what do you mean? Can you give an example of a Python expression, involving PEP 572 binding-expressions, that is not a tree but a more general DAG or that contains cycles? -- Steve ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
Łukasz Langa wrote: What was its own assignment before now is part of the logic test. This saves on vertical whitespace but makes parsing and understanding logic tests harder. Another way to say this is that expressions are no longer restricted to being trees, but can be general DAGs, which require more mental effort to understand. Hmmm, maybe they should be called "spaghetti expressions". :-) -- Greg ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
[Uncle T] > One language feature conspicuous by absence in newbie > confusions was, consistently, assignment expressions. Read any book > or tutorial for such a language, and you'll find very little space > devoted to them too. Well, you have an entire code style built around this feature called Yoda conditions. You teach people on Day 1 to never ever confuse == with =. Some compilers even warn about this because so many people did it wrong. > What's to learn? If they understand "binding a name" _at all_ (which > they must to even begin to write a non-trivial program), the only > twist is that a binding expression returns the value being bound. Ha, not in Python! Here we have *different syntax* for assignments in expressions. Well, you can also use it as a statement. But don't! We have a better one for that. And that one supports type annotations, can unpack and assign to many targets at the same time, and can even increment, multiply and so on, at once. But the other one can't. So only use the Pascal one in expressions. But don't forget parentheses, otherwise it will bind the thing you probably didn't want anyway. >> To my eyes, the examples give ample opportunity for being >> misunderstood and will create a need to puzzle-out the intended semantics. > > Some do, many don't. As soon as we have to wrap a part of an expression in parentheses, parsing the entire thing becomes more complex. Often enough it will cause the expression to exceed whatever line length limit the codebase pledged not to exceed, causing one line to become three. And again, making it trickier for a regular Łukasz to understand what's going on. -- Ł___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
[Raymond Hettinger] > After re-reading all the proposed code samples, I believe that > adopting the PEP will make the language harder to teach to people > who are not already software engineers. Can you elaborate on that? I've used dozens of languages over the decades, most of which did have some form of embedded assignment. Yes, I'm a software engineer, but I've always pitched in on "help forums" too. One language feature conspicuous by absence in newbie confusions was, consistently, assignment expressions. Read any book or tutorial for such a language, and you'll find very little space devoted to them too. What's to learn? If they understand "binding a name" _at all_ (which they must to even begin to write a non-trivial program), the only twist is that a binding expression returns the value being bound. Binding expressions certainly wouldn't be the _first_ thing to teach people. But by the time it would make sense to teach them, it's hard for me to grasp how a student could struggle with such a tiny variation on what they've already learned (all the subtleties are in what - exactly - "binding"means - which they already faced the first time they saw "j = 1"). > To my eyes, the examples give ample opportunity for being > misunderstood and will create a need to puzzle-out the intended semantics. Some do, many don't. The same can be said of a great many constructs ;-) > ... ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Is PEP 572 really the most effective way to solve the problems it's targeting?
On April 25, 2018 11:05:04 PM Steven D'Apranowrote: On Wed, Apr 25, 2018 at 09:36:31PM -0500, Ryan Gonzalez wrote: I have to say I'm not overly thrilled with PEP 572...it's almost odd, because if you asked me back when I first joined this list when I was 13, I would've no doubt said *YES*. I have the opposite experience: I've warmed to it the more I have read it. Before Chris had even written this PEP, there was a Python-Ideas thread asking for dedicated syntax in comprehensions alone that would have been equivalent to a binding-expression. I was rather negative about the whole thing (but no where near as negative as I've been in the past when this has come up before), until Chris pointed out that such binding-expressions have value outside of comprehensions. [...] Now, what's the common theme here? **Declarations should be separate from expressions.** Declarations and assignments are not the same thing. Yeah, this is what happens when I write these things when tired... I meant to also mention assignments here, and anywhere else I may have used "declaration". We've got languages that range from baggage-filled to functional to a bit of all of the above, and none of them have added assignment *inside* an expression. And your claim about assignment inside expressions is only true if you ignore the languages where assignment is an expression with a return value. You know, strange and exotic languages with hardly any users or influence, like C, C++, C#, Java, Javascript, Ruby, Perl, PHP, Lisp ... In the majority of these, however, mixing assignments into expressions is considered bad practice. For instance: - C++ core guidelines: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Res-complicated - MediaWiki style guide: https://m.mediawiki.org/wiki/Manual:Coding_conventions/PHP Some of the other languages (e.g. Perl) aren't some I'd regard as positive influences... -- Steve ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/rymg19%40gmail.com -- Ryan (ライアン) Yoko Shimomura, ryo (supercell/EGOIST), Hiroyuki Sawano >> everyone else https://refi64.com/ ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Is PEP 572 really the most effective way to solve the problems it's targeting?
[Ryan Gonzalez] > I have to say I'm not overly thrilled with PEP 572...it's almost odd, > because if you asked me back when I first joined this list when I was 13, I > would've no doubt said *YES*. But, since then, I've gone across many > projects and languages, and fundamentally *I have never felt hurt by the > lack of assignment in an expression*, and I always regretted every time I > tried it in C or Crystal. I understand this experience is pretty > insignificant in comparison to many of the wizards here, but I thought I'd > still share it as an opener for what I'm about to say. The older you get, the more you'll regret not still being 13 ;-) > With this being said, I'd encourage everyone to take a bit of a step back: > what exactly are people looking for in PEP 572? > > I see two main goals: > > - Assignment in a conditional structure. > - Assignment in a list comprehension. > > Most other use cases would significantly hurt readability and seem pretty > rare. I haven't been much impressed by suggested uses outside conditional contexts either. > Now let's break down the top one: > > - Assignment in an if condition. > - Assignment in a while condition. > > So there are roughly three main goals here overall. Now, are there better > ways to solve these? > ... > C++ has recently solved the if condition by allowing declarations inside the > conditions: But C++ has always had assignment expressions. This: > if (auto a = 123; a != 456) { is solving a different (albeit related) problem: that C/C++ require declaring variables before use. Python doesn't. They could have done the same via, .e.g,, { auto a = 123; if (a != 456) { ... } } and still have had the scope of `a` limited to one block. auto-initializers in conditionals just gave a bit of syntactic sugar for what was already easily (although with more typing) done. > Many languages have a 'let' expression (using Felix as my example): > > if let a = 1, b = 2 in a == b then I don't read Felix, but I assume the _scope_ of `a` & `b` there ends immediately before the "then". If the names can't be used in the _body_ of a Python `if` (or `while`) block, it's essentially useless to allow binding names for use solely in the conditional test. So it would help if you picked "real Python examples" from the many other earlier messages in these threads. Python expressions can't span Python statement boundaries - only Python blocks can do that. A form of `let` that _would_ work would be block-structured: let m = regexp.match(pattern. line) in: if m: print(m.group(0)) That solves "a scope problem" the current version of the PEP gave up on, but in all other respects seems a step back from the current: m = regexp.match(pattern, line) if m: print(m.group(0)) > Swift has taken a bit of a hybrid between the above two: > > if let a = 1, b = 2, a == b { That seems plain incoherent ;-) > Now, what's the common theme here? **Declarations should be separate from > expressions.** We've got languages that range from baggage-filled to > functional to a bit of all of the above, and none of them have added > assignment *inside* an expression. C++ and C have always had assignment expressions . Ditto Java, Javascript, Perl, Icon, ... (many, many others). I don't see a good reason to grant that Felix and Swift are necessarily improvements over the former (with the exception of Icon, which I'm merely fond of) very widely used languages. > The argument is roughly the same across all boards: you're putting major but > easy-to-miss side effects in the midst of expressions that *seem* pure. > > All this is to say: I'd really encourage everyone here to think a bit more > about *why* exactly you want this feature, and then think if there's really > no better way. Any solution that separates declarations would be far more > readable, (arguably) more Pythonic, and play more nicely with the new-ish > typing features to boot People have been trying for years. If you come up with a realistic (for Python) idea, that's great - share it! But it's probably better suited to python-ideas than python-dev. > ... ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Is PEP 572 really the most effective way to solve the problems it's targeting?
On Wed, Apr 25, 2018 at 09:36:31PM -0500, Ryan Gonzalez wrote: > > > I have to say I'm not overly thrilled with PEP 572...it's almost odd, > because if you asked me back when I first joined this list when I was 13, I > would've no doubt said *YES*. I have the opposite experience: I've warmed to it the more I have read it. Before Chris had even written this PEP, there was a Python-Ideas thread asking for dedicated syntax in comprehensions alone that would have been equivalent to a binding-expression. I was rather negative about the whole thing (but no where near as negative as I've been in the past when this has come up before), until Chris pointed out that such binding-expressions have value outside of comprehensions. [...] > Now, what's the common theme here? **Declarations should be separate from > expressions.** Declarations and assignments are not the same thing. > We've got languages that range from baggage-filled to > functional to a bit of all of the above, and none of them have added > assignment *inside* an expression. And your claim about assignment inside expressions is only true if you ignore the languages where assignment is an expression with a return value. You know, strange and exotic languages with hardly any users or influence, like C, C++, C#, Java, Javascript, Ruby, Perl, PHP, Lisp ... -- Steve ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
> On Apr 25, 2018, at 8:11 PM, Yury Selivanovwrote: > > FWIW I started my thread for allowing '=' in expressions to make sure that > we fully explore that path. I don't like ':=' and I thought that using '=' > can make the idea more appealing to myself and others. It didn't, sorry if > it caused any distraction. Although adding a new ':=' operator isn't my main > concern. > > I think it's a fact that PEP 572 makes Python more complex. > Teaching/learning Python will inevitably become harder, simply because > there's one more concept to learn. > > Just yesterday this snippet was used on python-dev to show how great the > new syntax is: > > my_func(arg, buffer=(buf := [None]*get_size()), size=len(buf)) > > To my eye this is an anti-pattern. One line of code was saved, but the > other line becomes less readable. The fact that 'buf' can be used after > that line means that it will be harder for a reader to trace the origin of > the variable, as a top-level "buf = " statement would be more visible. > > The PEP lists this example as an improvement: > > [(x, y, x/y) for x in input_data if (y := f(x)) > 0] > > I'm an experienced Python developer and I can't read/understand this > expression after one read. I have to read it 2-3 times before I trace where > 'y' is set and how it's used. Yes, an expanded form would be ~4 lines > long, but it would be simple to read and therefore review, maintain, and > update. > > Assignment expressions seem to optimize the *writing code* part, while > making *reading* part of the job harder for some of us. I write a lot of > Python, but I read more code than I write. If the PEP gets accepted I'll > use > the new syntax sparingly, sure. My main concern, though, is that this PEP > will likely make my job as a code maintainer harder in the end, not easier. > > I hope I explained my -1 on the PEP without sounding emotional. FWIW, I concur with all of Yuri's thoughtful comments. After re-reading all the proposed code samples, I believe that adopting the PEP will make the language harder to teach to people who are not already software engineers. To my eyes, the examples give ample opportunity for being misunderstood and will create a need to puzzle-out the intended semantics. On the plus side, the proposal does address the occasional minor irritant of writing an assignment on a separate line. On the minus side, the visual texture of the new code is less appealing. The proposal also messes with my mental model for the distinction between expressions and statements. It probably doesn't matter at this point (minds already seem to be made up), but put me down for -1. This is a proposal we can all easily live without. Raymond ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
On Wednesday, April 25, 2018, Łukasz Langawrote: > > On 25 Apr, 2018, at 5:20 PM, Chris Angelico wrote: > > On Thu, Apr 26, 2018 at 10:11 AM, Yury Selivanov > wrote: > > Just yesterday this snippet was used on python-dev to show how great the > new syntax is: > > my_func(arg, buffer=(buf := [None]*get_size()), size=len(buf)) > > To my eye this is an anti-pattern. One line of code was saved, but the > other line becomes less readable. The fact that 'buf' can be used after > that line means that it will be harder for a reader to trace the origin of > the variable, as a top-level "buf = " statement would be more visible. > > > Making 'buf' more visible is ONLY a virtue if it's going to be used > elsewhere. Otherwise, the name 'buf' is an implementation detail of > the fact that this function wants both a buffer and a size. > > > You're claiming that `:=` is nicer in this situation because it's less > prominent than regular assignment and thus doesn't suggest that the name > stays visible later. > > But as others said, `:=` *does* make the name visible later until the > enclosing scope ends. In fact, a large part of its appeal is that you > can use the result later (as in the `re.search()` example). Will it be > visible enough to the reaser in those cases then? > > There seems to be a conflict there. > > The question of assignment visibility also makes me think about > unintentional name shadowing:: > > buf = some_value > > ... # 20 lines > > my_func(arg, buffer=(buf := [None]*get_size()), size=len(buf)) > > ... # 20 lines > > buf # <-- What value does this have? > > > Even if we're not using the call pattern, there can be plenty of logic > tests which aren't very obvious:: > > buf = some_value > > ... # 20 lines > > if node.parent is not None and (buf := node.parent.buffer): > ... # 10 lines > > ... # 20 lines > > buf # <-- What value does this have? > > > This is even more interesting because now `buf` isn't rebound > *always*. > > So if I'm confused about an unexpected change in value of `buf`, I'll > skim the code, fail to find the assignment, and then grep for `buf =` > and also fail to find the assignment. Yes, I could have searched for > just `buf` instead but that will give me too many false positives, > especially if I'm using a primitive text editor search or don't know > about \b in regular expressions. > > Debugging this can be confusing. I know it can since a similar > annoyance can be observed with the magic pseudo-scope of `except`:: > > err = some_value > try: > ... > except Error as err: > ... > > err # <-- now sometimes it's not defined > > > Just like Barry, I debugged a few cases of this in the past and within > larger functions this can be hard to find. > Would this make it easier to put too much code on one line? Is there a good way to get *branch coverage* stats instead of just *line coverage*? Someone can probably explain with some tested pretty code for me why this would be necessary or helpful; why it wouldn't make line coverage stats more misleading for the sake of lazy? > > -- Ł > > ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
[Python-Dev] Is PEP 572 really the most effective way to solve the problems it's targeting?
I have to say I'm not overly thrilled with PEP 572...it's almost odd, because if you asked me back when I first joined this list when I was 13, I would've no doubt said *YES*. But, since then, I've gone across many projects and languages, and fundamentally *I have never felt hurt by the lack of assignment in an expression*, and I always regretted every time I tried it in C or Crystal. I understand this experience is pretty insignificant in comparison to many of the wizards here, but I thought I'd still share it as an opener for what I'm about to say. With this being said, I'd encourage everyone to take a bit of a step back: what exactly are people looking for in PEP 572? I see two main goals: - Assignment in a conditional structure. - Assignment in a list comprehension. Most other use cases would significantly hurt readability and seem pretty rare. Now let's break down the top one: - Assignment in an if condition. - Assignment in a while condition. So there are roughly three main goals here overall. Now, are there better ways to solve these? (FWIW C solved the while condition one with the C-style for loop, but I'm pretty sure few people here would really go for that.) C++ has recently solved the if condition by allowing declarations inside the conditions: if (auto a = 123; a != 456) { Many languages have a 'let' expression (using Felix as my example): if let a = 1, b = 2 in a == b then Swift has taken a bit of a hybrid between the above two: if let a = 1, b = 2, a == b { Now, what's the common theme here? **Declarations should be separate from expressions.** We've got languages that range from baggage-filled to functional to a bit of all of the above, and none of them have added assignment *inside* an expression. The argument is roughly the same across all boards: you're putting major but easy-to-miss side effects in the midst of expressions that *seem* pure. All this is to say: I'd really encourage everyone here to think a bit more about *why* exactly you want this feature, and then think if there's really no better way. Any solution that separates declarations would be far more readable, (arguably) more Pythonic, and play more nicely with the new-ish typing features to boot I understand reluctance to add a syntax exception like this, but I really feel it'd be worth it. As a side note, I was a strong supporter of comprehension generalizations, f-strings, *and* dataclasses. However, this proposal seems a bit ugly and excessive for what it's trying to accomplish. P.S. Yes, the unmatched curly braces were intentional to drive you crazy for a few hours. I blame Randall Monroe. You're welcome. -- Ryan (ライアン) Yoko Shimomura, ryo (supercell/EGOIST), Hiroyuki Sawano >> everyone else https://refi64.com/ ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
> On 25 Apr, 2018, at 5:20 PM, Chris Angelicowrote: > > On Thu, Apr 26, 2018 at 10:11 AM, Yury Selivanov > wrote: >> Just yesterday this snippet was used on python-dev to show how great the >> new syntax is: >> >> my_func(arg, buffer=(buf := [None]*get_size()), size=len(buf)) >> >> To my eye this is an anti-pattern. One line of code was saved, but the >> other line becomes less readable. The fact that 'buf' can be used after >> that line means that it will be harder for a reader to trace the origin of >> the variable, as a top-level "buf = " statement would be more visible. > > Making 'buf' more visible is ONLY a virtue if it's going to be used > elsewhere. Otherwise, the name 'buf' is an implementation detail of > the fact that this function wants both a buffer and a size. You're claiming that `:=` is nicer in this situation because it's less prominent than regular assignment and thus doesn't suggest that the name stays visible later. But as others said, `:=` *does* make the name visible later until the enclosing scope ends. In fact, a large part of its appeal is that you can use the result later (as in the `re.search()` example). Will it be visible enough to the reaser in those cases then? There seems to be a conflict there. The question of assignment visibility also makes me think about unintentional name shadowing:: buf = some_value ... # 20 lines my_func(arg, buffer=(buf := [None]*get_size()), size=len(buf)) ... # 20 lines buf # <-- What value does this have? Even if we're not using the call pattern, there can be plenty of logic tests which aren't very obvious:: buf = some_value ... # 20 lines if node.parent is not None and (buf := node.parent.buffer): ... # 10 lines ... # 20 lines buf # <-- What value does this have? This is even more interesting because now `buf` isn't rebound *always*. So if I'm confused about an unexpected change in value of `buf`, I'll skim the code, fail to find the assignment, and then grep for `buf =` and also fail to find the assignment. Yes, I could have searched for just `buf` instead but that will give me too many false positives, especially if I'm using a primitive text editor search or don't know about \b in regular expressions. Debugging this can be confusing. I know it can since a similar annoyance can be observed with the magic pseudo-scope of `except`:: err = some_value try: ... except Error as err: ... err # <-- now sometimes it's not defined Just like Barry, I debugged a few cases of this in the past and within larger functions this can be hard to find. -- Ł signature.asc Description: Message signed with OpenPGP ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
On Wed, Apr 25, 2018 at 8:22 PM Chris Angelicowrote: [..] > > my_func(arg, buffer=(buf := [None]*get_size()), size=len(buf)) > > > > To my eye this is an anti-pattern. One line of code was saved, but the > > other line becomes less readable. The fact that 'buf' can be used after > > that line means that it will be harder for a reader to trace the origin of > > the variable, as a top-level "buf = " statement would be more visible. > Making 'buf' more visible is ONLY a virtue if it's going to be used > elsewhere. Otherwise, the name 'buf' is an implementation detail of > the fact that this function wants both a buffer and a size. Should you > want to expand this out over more lines, you could do this: Chris, you didn't read that paragraph in my email to the end or I did a poor job at writing it. My point is that "buf" can still be used below that line, and therefore sometimes it will be used, as a result of quick refactoring or poor coding style. It's just how things happen when you write code: it gets rewritten and parts of it left outdated or not properly revised. *If* "buf" is used below that line it *will* be harder to find where it was initially set. Anyways, I don't want to distract everyone further so I'm not interested in continuing the discussion about what is readable and what is not. My own opinion on this topic is unlikely to change. I wanted to explain my -1; hopefully it will be noted. Yury ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
On Thu, 26 Apr 2018 10:20:40 +1000 Chris Angelicowrote: > On Thu, Apr 26, 2018 at 10:11 AM, Yury Selivanov > wrote: > > Just yesterday this snippet was used on python-dev to show how great the > > new syntax is: > > > > my_func(arg, buffer=(buf := [None]*get_size()), size=len(buf)) > > > > To my eye this is an anti-pattern. One line of code was saved, but the > > other line becomes less readable. The fact that 'buf' can be used after > > that line means that it will be harder for a reader to trace the origin of > > the variable, as a top-level "buf = " statement would be more visible. > > Making 'buf' more visible is ONLY a virtue if it's going to be used > elsewhere. Otherwise, the name 'buf' is an implementation detail of > the fact that this function wants both a buffer and a size. Should you > want to expand this out over more lines, you could do this: > > template = [None] > buf = template*get_size() > length = len(buf) > my_func(arg, buffer=buf, size=length) > > What are the names 'template' and 'length' achieving? Why should they > claim your attention? What is the name 'buf' in the binding expression achieving? Why should it claim my attention? It's not any different: it's just something that's used in a statement then unnecessary. Yet it will persist until the end of the enclosing scope, being retained for no reason. Perhaps we need C-like nested scopes, if such is the concern about names that live for too long? (of course, the fact that `my_func` needs you to pass its argument's length as a separate argument, while it could compute it by itself, is a bit silly) As a side note, personally, I'm usually much more concerned about the lifetime of *values* than the lifetime of names. The latter are cheap, the former can represent expensive resources. Regards Antoine. > They are useless relics of a done-and-dusted > calculation, being retained for no reason. They do not deserve > top-level placement. > > The form as given above is starting to get a bit noisy, but I strongly > disagree that 'buf' deserves to be a stand-alone name. It is as > valueless as 'template' is. > > ChrisA ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
On Thu, Apr 26, 2018 at 10:11 AM, Yury Selivanovwrote: > Just yesterday this snippet was used on python-dev to show how great the > new syntax is: > > my_func(arg, buffer=(buf := [None]*get_size()), size=len(buf)) > > To my eye this is an anti-pattern. One line of code was saved, but the > other line becomes less readable. The fact that 'buf' can be used after > that line means that it will be harder for a reader to trace the origin of > the variable, as a top-level "buf = " statement would be more visible. Making 'buf' more visible is ONLY a virtue if it's going to be used elsewhere. Otherwise, the name 'buf' is an implementation detail of the fact that this function wants both a buffer and a size. Should you want to expand this out over more lines, you could do this: template = [None] buf = template*get_size() length = len(buf) my_func(arg, buffer=buf, size=length) What are the names 'template' and 'length' achieving? Why should they claim your attention? They are useless relics of a done-and-dusted calculation, being retained for no reason. They do not deserve top-level placement. The form as given above is starting to get a bit noisy, but I strongly disagree that 'buf' deserves to be a stand-alone name. It is as valueless as 'template' is. ChrisA ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
On Wed, 25 Apr 2018 18:55:56 -0500 Tim Peterswrote: > > > the shorthand version appears completely bonkers. > > I wouldn't go that far, but I already said I wouldn't write it that way. > > However, without looking at real code, people are just flat-out > guessing about how bad - or good - things _can_ get, no matter how > confident they sound. > > So at least give me credit for presenting the _worst_ brief > binding-expression example you've seen too ;-) I had no idea you were a bit short on them, so I'll gladly give you credits for it :-) But I hope you'll use them responsibly! Regards Antoine. ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
On Wed, Apr 25, 2018 at 5:58 PM Guido van Rossumwrote: [..] > It was meant dismissive. With Chris, I am tired of every core dev starting their own thread about how PEP 572 threatens readability or doesn't reach the bar for new syntax (etc.). These arguments are entirely emotional and subjective. FWIW I started my thread for allowing '=' in expressions to make sure that we fully explore that path. I don't like ':=' and I thought that using '=' can make the idea more appealing to myself and others. It didn't, sorry if it caused any distraction. Although adding a new ':=' operator isn't my main concern. I think it's a fact that PEP 572 makes Python more complex. Teaching/learning Python will inevitably become harder, simply because there's one more concept to learn. Just yesterday this snippet was used on python-dev to show how great the new syntax is: my_func(arg, buffer=(buf := [None]*get_size()), size=len(buf)) To my eye this is an anti-pattern. One line of code was saved, but the other line becomes less readable. The fact that 'buf' can be used after that line means that it will be harder for a reader to trace the origin of the variable, as a top-level "buf = " statement would be more visible. The PEP lists this example as an improvement: [(x, y, x/y) for x in input_data if (y := f(x)) > 0] I'm an experienced Python developer and I can't read/understand this expression after one read. I have to read it 2-3 times before I trace where 'y' is set and how it's used. Yes, an expanded form would be ~4 lines long, but it would be simple to read and therefore review, maintain, and update. Assignment expressions seem to optimize the *writing code* part, while making *reading* part of the job harder for some of us. I write a lot of Python, but I read more code than I write. If the PEP gets accepted I'll use the new syntax sparingly, sure. My main concern, though, is that this PEP will likely make my job as a code maintainer harder in the end, not easier. I hope I explained my -1 on the PEP without sounding emotional. Thank you, Yury Yury ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Visual similarity of "=" and "==" (in context of PEP 572)
On Thu, Apr 26, 2018 at 9:54 AM, Mikhail Vwrote: > Since the discussion about operator choice has completely migrated > here, I'll put my note also here. > From the very beginning of PEP 572 discussion I have noticed > a strange fact - there is told a lot about visual similarity > of "=" and "==" in Python. > *Same is told in the PEP 572 (frequently asked question) > as the reason for rejection of "=" as operator.* > > Hope you get my point here. > IMO either the PEP 572 should remove or at > least rephrase this point somehow or, even better, > concentrate on something more convincing in this regard. https://www.python.org/dev/peps/pep-0572/#why-not-just-turn-existing-assignment-into-an-expression Half a century of C programming demonstrates that this is ample argument. ChrisA ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
[Python-Dev] Visual similarity of "=" and "==" (in context of PEP 572)
Since the discussion about operator choice has completely migrated here, I'll put my note also here. >From the very beginning of PEP 572 discussion I have noticed a strange fact - there is told a lot about visual similarity of "=" and "==" in Python. *Same is told in the PEP 572 (frequently asked question) as the reason for rejection of "=" as operator.* I realize that currently there are other technical arguments against "=", but I am the opinion that this very reason, namely similarity of "=" and "==", cannot be taken seriously. I'll try to explain: I have opened Notepad++ and went to syntax highlighting configurator. It took me 2 minutes to choose a syntax setup that defines "==" (and other comparison operators if you wish) as a token with custom style. Now I can apply *custom color and size* to it and this alone makes the operator very well seen and different from "=". If that is not enough, I can set different *font* for the operator: so I can basically turn "==" into anything I want. With this in mind, I personally can't understand why you complain about "=="? Ok, you could say - not all editors can do this. But even without that tweaks - I still don't find the problem worth exaggeration. It is not like I accidentally choose very similar names for variables and then spend an hour trying to figure out where is the bug comes from. With "==" it just takes some attention to look at -if- statements and recheck if I write "==" and not "=" where I need comparison. I see it as as a "typo-magnet" but no way as a "bug-magnet" as it was claimed repeatedly in the discussion. A programmer already knows about this similarity and can fix the typos easily. The ":=" operator is of course slightly more different, but still, only _slightly_ better in this regard. With that said, I am not much interested in either adding of the expression assignment, although I am really worrying about the reasoning made about "=" and "==" in the discussion. Hope you get my point here. IMO either the PEP 572 should remove or at least rephrase this point somehow or, even better, concentrate on something more convincing in this regard. Also I think Yury has made good point about the introduction of ":=" -- it would co-exist with the "=". So imo, if you get familiar with ":=", later on it will just cause additional visual noise. And I don't think a regular user will be much interested in nuances of internal implementation of new assignment - they'll just follow the logic of the code and another operator will constantly gain unnecessary attention at reading. Mikhail ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
[Tim] To my eyes, this is genuinely harder to follow, despite its relative brevity: while total != (total := total + term): [Antoine] >>> Does it even work? Perhaps if the goal is to stop when total is NaN, >>> but otherwise? [Chris] >> Yes, it does, because the first "total" is looked up before the >> rebinding happens. It's 100% unambiguous to the compiler... but still >> pretty unclear to a human. And I think the multiple use of 'total' is >> to blame for that. So I agree with Tim that this particular example is >> better in longhand. [Antoine] > "Better" is an understatement :-( Now that I understood it (thanks > for the explanation), Ah, sorry - I had no idea it was the "left to right evaluation" part you weren't seeing. Next time explain why you think something is broken? > the shorthand version appears completely bonkers. I wouldn't go that far, but I already said I wouldn't write it that way. However, without looking at real code, people are just flat-out guessing about how bad - or good - things _can_ get, no matter how confident they sound. So at least give me credit for presenting the _worst_ brief binding-expression example you've seen too ;-) ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] The new and improved PEP 572, same great taste with 75% less complexity!
On Thu, Apr 26, 2018 at 9:05 AM, Victor Stinnerwrote: >> # Handle a matched regex >> if (match := pattern.search(data)) is not None: >> ... >> >> # A more explicit alternative to the 2-arg form of iter() invocation >> while (value := read_next_item()) is not None: >> ... >> >> # Share a subexpression between a comprehension filter clause and its >> output >> filtered_data = [y for x in data if (y := f(x)) is not None] > > What do you think of adding the variant without the new ":=" syntax? > It would help to see the benefit of the new ":=" syntax, and help to > compare the two syntaxes. > > if: > if (match := pattern.search(data)) is not None: ... > vs > match = pattern.search(data) > if match is not None: ... > > while: > while (value := read_next_item()) is not None: ... > vs > while True: > value = read_next_item() > if value is None: break > ... > > list-comprehension: > filtered_data = [y for x in data if (y := f(x)) is not None] > vs > filtered_data = [f(x) for x in data] > filtered_data = [x for x in filtered_data if x is not None] Doing that for everything would put the PEP solidly into "TL;DR" territory, I'm afraid. There's already too much verbiage in some of those sections. Plus, we'd get right back into debates about how if you just change your intended semantics slightly, there's a completely different way to achieve something actually quite different, and we've already been around that a few times already. ChrisA ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] The new and improved PEP 572, same great taste with 75% less complexity!
> # Handle a matched regex > if (match := pattern.search(data)) is not None: > ... > > # A more explicit alternative to the 2-arg form of iter() invocation > while (value := read_next_item()) is not None: > ... > > # Share a subexpression between a comprehension filter clause and its > output > filtered_data = [y for x in data if (y := f(x)) is not None] What do you think of adding the variant without the new ":=" syntax? It would help to see the benefit of the new ":=" syntax, and help to compare the two syntaxes. if: if (match := pattern.search(data)) is not None: ... vs match = pattern.search(data) if match is not None: ... while: while (value := read_next_item()) is not None: ... vs while True: value = read_next_item() if value is None: break ... list-comprehension: filtered_data = [y for x in data if (y := f(x)) is not None] vs filtered_data = [f(x) for x in data] filtered_data = [x for x in filtered_data if x is not None] Victor ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
On Thu, 26 Apr 2018 08:38:51 +1000 Chris Angelicowrote: > On Thu, Apr 26, 2018 at 8:08 AM, Antoine Pitrou wrote: > > On Wed, 25 Apr 2018 16:55:43 -0500 > > Tim Peters wrote: > >> > >> To my eyes, this is genuinely harder to follow, despite its relative > >> brevity: > >> > >> while total != (total := total + term): > > > > Does it even work? Perhaps if the goal is to stop when total is NaN, > > but otherwise? > > Yes, it does, because the first "total" is looked up before the > rebinding happens. It's 100% unambiguous to the compiler... but still > pretty unclear to a human. And I think the multiple use of 'total' is > to blame for that. So I agree with Tim that this particular example is > better in longhand. "Better" is an understatement :-( Now that I understood it (thanks for the explanation), the shorthand version appears completely bonkers. Regards Antoine. ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
On Wed, Apr 25, 2018 at 3:15 PM, Ethan Furmanwrote: > On 04/25/2018 02:55 PM, Tim Peters wrote: > >> To my eyes, this is genuinely harder to follow, despite its relative >> brevity: >> >> while total != (total := total + term): >> term *= mx2 / (i*(i+1)) >> i += 2 >> return total >> >> So I wouldn't use binding expressions in that case. I don't have a >> compelling head argument for _why_ I find the latter spelling harder >> to follow, but I don't need a theory to know that I in fact do. >> > > I know why I do: I see "while total != total" and my gears start > stripping. On the other hand, > > while total != (total + term as total): > ... > > I find still intelligible. (Yes, I know "as" is dead, just wanted to > throw that out there.) > The problem with either variant is that they hinge on subtle left-to-right evaluation rules. Python tries to promise left-to-right evaluation "except when it doesn't apply", e.g. in assignments the RHS is typically evaluated before subexpressions in the LHS: a[f()] = g() calls g() before f(). The example is supposed to load the left operand to != on the stack before loading the right operand, but the rule that says the left operand is evaluated before the right operand is much weaker than other evaluation order rules (like the rule stating that the arguments are evaluated before the function is called -- and before you laugh, in Algol-60 that wasn't always the case). This argument applies regardless of which syntactic form you use, and no matter what we choose, the PEP will have to clarify evaluation order in more cases than the current reference manual. (IIRC Nathaniel brought this up.) -- --Guido van Rossum (python.org/~guido) ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
On Thu, Apr 26, 2018 at 8:08 AM, Antoine Pitrouwrote: > On Wed, 25 Apr 2018 16:55:43 -0500 > Tim Peters wrote: >> >> To my eyes, this is genuinely harder to follow, despite its relative brevity: >> >> while total != (total := total + term): > > Does it even work? Perhaps if the goal is to stop when total is NaN, > but otherwise? Yes, it does, because the first "total" is looked up before the rebinding happens. It's 100% unambiguous to the compiler... but still pretty unclear to a human. And I think the multiple use of 'total' is to blame for that. So I agree with Tim that this particular example is better in longhand. ChrisA ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
On 04/25/2018 03:15 PM, Ethan Furman wrote: On 04/25/2018 02:55 PM, Tim Peters wrote: This becomes a question of seasoned judgment. For example, here's a real loop summing a series expansion, until the new terms become so small they make no difference to the running total (a common enough pattern in code slinging floats or decimals): while True: old = total total += term if old == total: return total term *= mx2 / (i*(i+1)) i += 2 To my eyes, this is genuinely harder to follow, despite its relative brevity: while total != (total := total + term): term *= mx2 / (i*(i+1)) i += 2 return total So I wouldn't use binding expressions in that case. I don't have a compelling head argument for _why_ I find the latter spelling harder to follow, but I don't need a theory to know that I in fact do. I know why I do: I see "while total != total" and my gears start stripping. On the other hand, while total != (total + term as total): ... I find still intelligible. (Yes, I know "as" is dead, just wanted to throw that out there.) Having said that, since whomever mentioned reading ":=" as "which is", I'm good with ":=". -- ~Ethan~ ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
[Tim] >> To my eyes, this is genuinely harder to follow, despite its relative brevity: >> >> while total != (total := total + term): [Antoine] > Does it even work? Perhaps if the goal is to stop when total is NaN, > but otherwise? I don't follow you. You snipped all the text explaining why it would work, so trying reading that again? When, e.g., `total` reaches 1.0 and `term` reaches 1e-30, this becomes: while 1.0 != (total := 1.0 + 1-e30): which leaves `total` unchanged (1.0 + 1e-30 == 1.0) and then while 1.0 != 1.0: causes the loop to exit (`while False:`). >> For that reason, the messages that sway me are those showing real >> code, or at least plausibly realistic code. In the majority of those >> so far, binding expressions would be a small-to-major win. > I'm sure it's possible to find thousands of line of code where binding > expressions wouldn't be a win, but I'm not sure that would be a > constructive use of mailing-list bandwidth. And that "argument" is? ;-) Note that I managed to move the PEP _away_ from general "assignment expressions" to the much simpler "binding expressions" precisely _by_ illustrating, via real code, why the generality of the former wasn't actually useful in any case I looked at. If something is always - or almost always - useless, that can be shown via considering realistic code. That was far more productive than endless abstract debates. ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
On Wed, Apr 25, 2018 at 01:55:37PM -0700, Łukasz Langa wrote: > > > On 25 Apr, 2018, at 1:28 PM, Guido van Rossumwrote: > > > > You don't seem to grasp the usability improvements this will give. I hear > > you but at this point appeals to Python's "Zen" don't help you. > > This reads dismissive to me. I did read the PEP and followed the discussion on > python-dev. I referred to PEP 20 because it distills what's unique about the > value proposition of Python. It's our shared vocabulary. Every programming language has a shared vocabulary. That's hardly unique to Python. > Can you address the specific criticism I had? To paraphrase it without PEP 20 > jargon: > > > (name := expression) makes code less uniform. It inserts more information > > into a place that is already heavily packed with information (logic tests). I'm not Guido, but I'll make an attempt. I think the comment about "less uniform" isn't meaningful. Uniform in what way? I don't even know how to interpret the uniform comment here, unless you mean to imply that every statement and expression in Python currently has the same information density, and binding-expressions will violate that. That's clearly not the case, so I'm left puzzled by what you mean. As for your observation that binding-expressions don't reduce complexity, they merely move it, I think you may be right. But then it is a truism that complexity is never reduced, only moved, so that's likely to be true for any feature (including existing ones). Should we move back to assembly language programming because Python hasn't reduced complexity, only moved it? I don't think so. Clearly binding-expressions do add a little more complexity to the language, and they do move code from vertically separated statements to horizontally laid-out expressions. But why is this necessarily a bad thing? Exactly the same complaint can be made about comprehensions, and look at how wildly successful they have been. Offset against the increase in horizontal complexity is a corresponding decrease in vertical complexity, and that's beneficial. Whatever cost they have has to be offset against the benefits, and I think the feature will come ahead on the plus side overall. Of course, like any syntactic feature, it may be abused by those who (by accident or design) write obfuscated or excessively complex code. We shouldn't ignore that risk, but nor should we use that as an excuse to dismiss the feature's benefits. -- Steve ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
On 04/25/2018 02:55 PM, Tim Peters wrote: This becomes a question of seasoned judgment. For example, here's a real loop summing a series expansion, until the new terms become so small they make no difference to the running total (a common enough pattern in code slinging floats or decimals): while True: old = total total += term if old == total: return total term *= mx2 / (i*(i+1)) i += 2 To my eyes, this is genuinely harder to follow, despite its relative brevity: while total != (total := total + term): term *= mx2 / (i*(i+1)) i += 2 return total So I wouldn't use binding expressions in that case. I don't have a compelling head argument for _why_ I find the latter spelling harder to follow, but I don't need a theory to know that I in fact do. I know why I do: I see "while total != total" and my gears start stripping. On the other hand, while total != (total + term as total): ... I find still intelligible. (Yes, I know "as" is dead, just wanted to throw that out there.) -- ~Ethan~ ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
On Wed, 25 Apr 2018 16:55:43 -0500 Tim Peterswrote: > > To my eyes, this is genuinely harder to follow, despite its relative brevity: > > while total != (total := total + term): Does it even work? Perhaps if the goal is to stop when total is NaN, but otherwise? > For that reason, the messages that sway me are those showing real > code, or at least plausibly realistic code. In the majority of those > so far, binding expressions would be a small-to-major win. I'm sure it's possible to find thousands of line of code where binding expressions wouldn't be a win, but I'm not sure that would be a constructive use of mailing-list bandwidth. Regards Antoine. ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
On Wed, Apr 25, 2018 at 1:55 PM, Łukasz Langawrote: > > > On 25 Apr, 2018, at 1:28 PM, Guido van Rossum wrote: > > > > You don't seem to grasp the usability improvements this will give. I > hear you but at this point appeals to Python's "Zen" don't help you. > > This reads dismissive to me. I did read the PEP and followed the > discussion on > python-dev. It was meant dismissive. With Chris, I am tired of every core dev starting their own thread about how PEP 572 threatens readability or doesn't reach the bar for new syntax (etc.). These arguments are entirely emotional and subjective. And that's how big decisions get made. Nobody can predict the outcome with sufficient accuracy. It's like buying a new car or house. In the end you decide with your gut. > I referred to PEP 20 because it distills what's unique about the > value proposition of Python. It's our shared vocabulary. > It's poetry, not a set of axioms. You can't *prove* anything with an appeal to PEP 20. You can appeal to it, for sure, but such an appeal *by definition* is subjective and emotional. (There's Only One Way To Do It? Give me a break. :-) > Can you address the specific criticism I had? To paraphrase it without PEP > 20 > jargon: > > > (name := expression) makes code less uniform. It inserts more > information > > into a place that is already heavily packed with information (logic > tests). > Most Python features make code less uniform in order to make it less repetitive. (Who needs classes? :-) -- --Guido van Rossum (python.org/~guido) ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
[Guido] >> You don't seem to grasp the usability improvements this will give. >> I hear you but at this point appeals to Python's "Zen" don't help you. [Łukasz Langa] > This reads dismissive to me. I did read the PEP and followed the discussion on > python-dev. I referred to PEP 20 because it distills what's unique about the > value proposition of Python. It's our shared vocabulary. > > Can you address the specific criticism I had? To paraphrase it without PEP 20 > jargon: > (name := expression) makes code less uniform. It inserts more information > into a place that is already heavily packed with information (logic tests). I'll take a crack at that. It's not about "head arguments" at all. I sat out the first hundred messages about this on python-ideas, and looked at code instead. What I found had little to do with any of the head (abstract) arguments passionately debated for the duration ;-) In real life, I found a great many conditional tests that not only weren't "heavily packed" with information, they were simply of the form: NAME = expression if NAME: ... use NAME ... That looks more like assembly language than Python ;-) I saw no harm at all, and a little gain, in if NAME := expression: ... use NAME ... instead. But even a little gain adds up when it happens so often. Of course there have been better examples given of bigger gains. But in no case have the tests in those examples been "heavily packed with information". If they had been, I would have suggested instead breaking the test clauses _out_ of the conditional statements, and giving them names each on their own dedicated lines, with comments explaining what the heck the _intents_ are, even at the cost of adding an indentation level or two. Sometimes conditionals are _already_ "too dense". But more often they're very sparse. This becomes a question of seasoned judgment. For example, here's a real loop summing a series expansion, until the new terms become so small they make no difference to the running total (a common enough pattern in code slinging floats or decimals): while True: old = total total += term if old == total: return total term *= mx2 / (i*(i+1)) i += 2 To my eyes, this is genuinely harder to follow, despite its relative brevity: while total != (total := total + term): term *= mx2 / (i*(i+1)) i += 2 return total So I wouldn't use binding expressions in that case. I don't have a compelling head argument for _why_ I find the latter spelling harder to follow, but I don't need a theory to know that I in fact do. But neither do I need a compelling head argument for "why" to know that in many other cases I find that the use of binding expressions improves the code. You shouldn't believe me even if I pretended to have one and passionately argued for it. But, by the same token, I'm spectacularly unmoved by other peoples' head arguments. For that reason, the messages that sway me are those showing real code, or at least plausibly realistic code. In the majority of those so far, binding expressions would be a small-to-major win. ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 572: Write vs Read, Understand and Control Flow
On Wed, Apr 25, 2018 at 2:17 PM, Terry Reedywrote: > On 4/25/2018 6:10 AM, Steve Holden wrote: >> Indeed, in the cases where I currently find myself unwrapping expressions >> to capture their values in local variables for debugging purposes it would >> usually be far less intrusive to bind a name to the expression inline, then >> use the debugger to inspect the value. > > > I agree that this is a definite plus feature. Being able to tag > subexpressions would make visual debuggers that show all local variables as > one steps (like IDLE's) even more useful relative to print statements. Some other programming languages (thinking of Racket) solve this by having the debugger let you step through expression evaluation, without editing the code. e.g. in the line x = 1 + 2 * 3, we might step through and first evaluate 2*3 (-> 6), and then 1 + (-> 7). Similar to how Python already lets you step into and see the result of function calls. This is especially powerful in visual debuggers, where the stepping and output can be displayed very intuitively. -- Devin ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 572: Write vs Read, Understand and Control Flow
On 4/25/2018 6:10 AM, Steve Holden wrote: On Wed, Apr 25, 2018 at 4:56 AM, Tim Peters> wrote: [Tim] >> Binding expressions are debugger-friendly in that they _don't_ just >> vanish without a trace. It's their purpose to _capture_ the values of >> the expressions they name. Indeed, you may want to add them all over >> the place inside expressions, never intending to use the names, just >> so that you can see otherwise-ephemeral intra-expression results in >> your debugger ;-) [Steven D'Aprano >] wrote: > That's a fantastic point and I'm surprised nobody has thought of it > until now (that I've seen). > > Chris, if you're still reading this and aren't yet heartedly sick and > tired of the PEP *wink* this ought to go in as another motivating point. You know, I thought I was joking when I wrote that - but after I sent it I realized I wasn't ;-) You just don't realise how perspicacious you truly are, Tim! It would actually be quite convenient, and far less error-prone, to add a binding construct inside a complicated expression for purposes of running under a debugger. The alternative is typing the sub-expression(s) of interest by hand at the debugger prompt, or adding print()s, both of which are prone to introducing typos, or changing results radically due to triggering side effects in the code invoked by the duplicated sub-expression(s). Adding a binding construct wouldn't change anything about how the code worked (apart from possibly clobbering a local name). Indeed, in the cases where I currently find myself unwrapping expressions to capture their values in local variables for debugging purposes it would usually be far less intrusive to bind a name to the expression inline, then use the debugger to inspect the value. I agree that this is a definite plus feature. Being able to tag subexpressions would make visual debuggers that show all local variables as one steps (like IDLE's) even more useful relative to print statements. -- Terry Jan Reedy ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
> On 25 Apr, 2018, at 1:24 PM, Chris Angelicowrote: > > On Thu, Apr 26, 2018 at 6:21 AM, Łukasz Langa wrote: >> := also goes against having one obvious way to do it. Since it's an >> expression, >> it can also be placed on its own line or in otherwise weird places like >> function call arguments. I anticipate PEP 8 would have to be extended to >> explicitly discourage such abuse. Linters would grow rules against it. This >> is >> noise. > > Does this argument also apply to the if/else expression? Do linters > need rules to advise against people writing code like: > > print(x) if x is None else print(y) > > ? It's perfectly legal to write code like this. But I don't see people > abusing this sort of thing. Ternary expressions are different because their flow is deliberately different from a regular if statement. It's also different from the C equivalent. `:=` on the other hand is deciptively similar to `=`. But yeah, I think worrying about abuse of the feature is a red herring. The gist of my criticism of your PEP is about the decreased balance in information density. -- Ł signature.asc Description: Message signed with OpenPGP ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
> On 25 Apr, 2018, at 1:28 PM, Guido van Rossumwrote: > > You don't seem to grasp the usability improvements this will give. I hear you > but at this point appeals to Python's "Zen" don't help you. This reads dismissive to me. I did read the PEP and followed the discussion on python-dev. I referred to PEP 20 because it distills what's unique about the value proposition of Python. It's our shared vocabulary. Can you address the specific criticism I had? To paraphrase it without PEP 20 jargon: > (name := expression) makes code less uniform. It inserts more information > into a place that is already heavily packed with information (logic tests). -- Ł signature.asc Description: Message signed with OpenPGP ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
A very emotional appeal, you don't seem to grasp the usability improvements this will give. I hear you but at this point appeals to Python's "Zen" don't help you. On Wed, Apr 25, 2018 at 1:21 PM, Łukasz Langawrote: > PEP 572 caused a strong emotional reaction in me. I wanted to first > understand > my intuitive objection to the idea before posting anything. > > I feel that (name := expression) doesn't fit the narrative of PEP 20. It > doesn't remove complexity, it only moves it. What was its own assignment > before > now is part of the logic test. This saves on vertical whitespace but makes > parsing and understanding logic tests harder. This is a bad bargain: logic > tests already contain a lot of complexity that human readers have to cope > with. > > Proponents of := argue it makes several patterns flatter (= better than > nested) > to express. Serial regular expression matching is a popular example. > However, > (name := expression) itself is making logic tests more nested, not > flatter. It > makes information in the logic test denser (= worse than sparse). Since it > also > requires an additional pair of parentheses, it forces the reader to > decompose > the expression in their head. > > := also goes against having one obvious way to do it. Since it's an > expression, > it can also be placed on its own line or in otherwise weird places like > function call arguments. I anticipate PEP 8 would have to be extended to > explicitly discourage such abuse. Linters would grow rules against it. > This is > noise. > > I'm -1 on PEP 572, I think it's very similar in spirit to the rejected PEP > 463. > > -- Ł > > > ___ > Python-Dev mailing list > Python-Dev@python.org > https://mail.python.org/mailman/listinfo/python-dev > Unsubscribe: https://mail.python.org/mailman/options/python-dev/ > guido%40python.org > > -- --Guido van Rossum (python.org/~guido) ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
On Thu, Apr 26, 2018 at 6:21 AM, Łukasz Langawrote: > := also goes against having one obvious way to do it. Since it's an > expression, > it can also be placed on its own line or in otherwise weird places like > function call arguments. I anticipate PEP 8 would have to be extended to > explicitly discourage such abuse. Linters would grow rules against it. This is > noise. Does this argument also apply to the if/else expression? Do linters need rules to advise against people writing code like: print(x) if x is None else print(y) ? It's perfectly legal to write code like this. But I don't see people abusing this sort of thing. ChrisA ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
[Python-Dev] (name := expression) doesn't fit the narrative of PEP 20
PEP 572 caused a strong emotional reaction in me. I wanted to first understand my intuitive objection to the idea before posting anything. I feel that (name := expression) doesn't fit the narrative of PEP 20. It doesn't remove complexity, it only moves it. What was its own assignment before now is part of the logic test. This saves on vertical whitespace but makes parsing and understanding logic tests harder. This is a bad bargain: logic tests already contain a lot of complexity that human readers have to cope with. Proponents of := argue it makes several patterns flatter (= better than nested) to express. Serial regular expression matching is a popular example. However, (name := expression) itself is making logic tests more nested, not flatter. It makes information in the logic test denser (= worse than sparse). Since it also requires an additional pair of parentheses, it forces the reader to decompose the expression in their head. := also goes against having one obvious way to do it. Since it's an expression, it can also be placed on its own line or in otherwise weird places like function call arguments. I anticipate PEP 8 would have to be extended to explicitly discourage such abuse. Linters would grow rules against it. This is noise. I'm -1 on PEP 572, I think it's very similar in spirit to the rejected PEP 463. -- Ł signature.asc Description: Message signed with OpenPGP ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
[Python-Dev] PEP 394 update proposal: Allow changing the `python` command in some cases
Hello, In Fedora, I found that PEP 394's strict recommendation that `python` points to `python2` is holding us back. From discussions on Zulip and elsewhere it's clear that this recommendation is not changing any time soon, but I would like to officially relax it in several cases. The problems are: - For developers that are not following the language's development, the fact that `python` invokes `python2` sends a strong signal that 2 is somehow the preferred version, and it's OK to start new projects in it. - Users and sysadmins that *do* want to “live in the future” are switching the symlink to `python3` themselves. We would like to give them a supported, documented way to do so -- and make surer they're aware of the caveats. - The `python` command is still not available out-of-the box on macOS, so it didn't completely live up to the expectation of being the cross-platform way to launch 2/3 source compatile scripts. - `python` in the shebang line can mean *either* that a script is carefully written to be 2/3 compatible, *or* that the author/maintainer is lazy or unaware of the recommendations. While Fedora guidelines have long banned the unversioned command, we feel that the only way to *enforce* that guidelines is to provide environments where the `python` command does not work (unless explicitly installed). To help solve these, I would like to relax recommendations on the Unix ``python -> python2`` symlink in these cases: - Users and administrators can, by a deliberate action, change ``python`` to invoke Python 3. (Activating a venv counts as such an action, but so would e.g. using alternates, installing a non-default overriding package, or replacing /usr/bin/python.) - In controlled environments where being explicit is valued more than user experience (test environments, build systems, etc.), distributions can omit the `python` command even when `python2` is installed. I have filed these changes as a pull request here: https://github.com/python/peps/pull/630 The PR also spells out several other things, which I felt were hidden between the lines -- but correct me if you disagree with my reading. ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 573 -- Module State Access from C Extension Methods
On 04/25/18 14:46, Jeroen Demeyer wrote: On 2018-04-25 20:33, Petr Viktorin wrote: Perhaps "m_objclass" could point to the module in this case That was exactly my idea also today. Instead of treating m_objclass as the defining class, we should generalize it to be the "parent" of the function: either the class or the module. Great to hear we think alike. However, I think that while reusing the pointer is nice to save space, the two concepts should still be separate, because "defining module" is a reasonable concept even for methods. In particular: - There should be *separate* accessor functions for: - getting the defining class - getting the defining module - The latter would later (in PEP 573) be extended to return the defining module even for class methods (when available) - In Python code, __objclass__ should be the defining class, not the module. - The C field should have a different name (m_parent?), so it isn't that strongly associated with __objclass__. Does that sound reasonable? ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 573 -- Module State Access from C Extension Methods
On 2018-04-25 20:33, Petr Viktorin wrote: Perhaps "m_objclass" could point to the module in this case That was exactly my idea also today. Instead of treating m_objclass as the defining class, we should generalize it to be the "parent" of the function: either the class or the module. ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 573 -- Module State Access from C Extension Methods
On 04/24/18 13:12, Jeroen Demeyer wrote: On 2018-04-24 16:34, Jeroen Demeyer wrote: On the other hand, if you are passing the function object, then you can get __self__ from it (unless it's an unbound method: in that case __self__ is NULL and self is really args[0]). So there wouldn't be a need for passing "self". I'm not saying that this is better than passing "self" explicitly... I haven't yet decided what is best. One thing I realized from PEP 573: the fact that __self__ for built-in functions is set to the module is considered a feature. I never understood the reason for it (and I don't know if the original reason was the same as the reason in PEP 573). If we want to continue supporting that and we also want to support __get__ for built-in functions (to make them act as methods), then there are really two "selfs": there is the "self" from the method (the object that it's bound to) and the "self" from the built-in function (the module). To support that, passing *both* the function and "self" seems like the best way. You're talking about functions with METH_BINDING here, right? There the other "self" would be the defining module. It might make sense to pass that also in the struct, rather than as an additional argument. Perhaps "m_objclass" could point to the module in this case, or a new pointer could be added. ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] assignment expressions: an alternative alternative proposal
On Thu, Apr 26, 2018 at 1:46 AM, Guido van Rossumwrote: > On Wed, Apr 25, 2018 at 2:27 AM, Steve Holden wrote: >> >> On Wed, Apr 25, 2018 at 6:16 AM, Steven D'Aprano >> wrote: >>> >>> On Tue, Apr 24, 2018 at 03:54:30PM -0700, Guido van Rossum wrote: >>> >>> > We should really take this back to python-ideas at this point. >>> >>> Please no :-( >> >> >> +1 > > > Maybe I should have just said "no". Thanks Steven D'A for saving everyone > yet another detour on this proposal. > > I'm pretty excited myself about NAME := and am mostly ignoring > the current crop of counter-proposal. Of course, if someone wants to start a python-bad-ideas mailing list, I'm sure some of these alternatives would be perfect for it... If anyone hasn't seen the latest iteration of the PEP, I recently re-posted it. And it's always available here: https://www.python.org/dev/peps/pep-0572/ ChrisA ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] assignment expressions: an alternative alternative proposal
On Wed, Apr 25, 2018 at 2:27 AM, Steve Holdenwrote: > On Wed, Apr 25, 2018 at 6:16 AM, Steven D'Aprano > wrote: > >> On Tue, Apr 24, 2018 at 03:54:30PM -0700, Guido van Rossum wrote: >> >> > We should really take this back to python-ideas at this point. >> >> Please no :-( >> > > +1 > Maybe I should have just said "no". Thanks Steven D'A for saving everyone yet another detour on this proposal. I'm pretty excited myself about NAME := and am mostly ignoring the current crop of counter-proposal. -- --Guido van Rossum (python.org/~guido) ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 572: Assignment Expressions
On Apr 24, 2018, at 2:10 PM, MRABwrote: > > On 2018-04-21 03:15, Tim Peters wrote: >> [Tim] >> >> And I'll take this opportunity to repeat the key point for me: I >> >> tried hard, but never found a single case based on staring at real >> >> code where allowing _fancier_ (than "plain name") targets would be a >> >> real improvement. In every case I thought it _might_ help, it turned >> >> out that it really didn't unless Python _also_ grew an analog to C's >> >> "comma operator" (take only the last result from a sequence of >> >> expressions). I'll also note that I asked if anyone else had a >> >> real-life example, and got no responses. >> >> [MRAB ] >> > Could a semicolon in a parenthesised expression be an equivalent to C's >> > "comma operator"? >> >> I expect it could, but I it's been many years since I tried hacking >> Python's grammar, and I wouldn't want a comma operator anyway ;-) > [snip] > Just reading this: > > https://www.bfilipek.com/2018/04/refactoring-with-c17-stdoptional.html > > about C++17, and what did I see? An example with a semicolon in parentheses! A similar pattern shows up in Go's if statement syntax. It is interesting to note that it is part of the grammar specifically for the if statement and *not* general expression syntax. IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] . Bindings that occur inside of `SimpleStmt` are only available within the `Expression` and blocks that make up the if statement. https://golang.org/ref/spec#If_statements This isn't a good reason to parrot the syntax in Python though. IMO, I consider the pattern to be one of the distinguishing features of golang and would be happy leaving it there. I have often wondered if adding the venerable for loop syntax from C (and many other languages) would solve some of the needs here though. The for loop syntax in golang is interesting in that it serves as both a standard multipart for statement as well as a while statement. Changing something like this is more of a Python 4 feature and I think that I would be -0 on the concept. I did want to mention the similarities for the posterity though. ChrisA - we might want to add explicit mentions of golang's if statement and for loop as "considered" syntaxes since they are in a sibling programing language (e.g., similar to async/await in PEP 492). - dave -- "Syntactic sugar causes cancer of the semicolon" - Alan Perlis___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
[Python-Dev] Meta-question about this mailing list
Hi, the description on https://mail.python.org/mailman/listinfo/python-dev reads: "On this list the key Python developers discuss the future of the language and its implementation." I interpret this sentence as "only key (core?) developers are supposed to participate in the discussion (ie. post messages), the rest is welcome to lurk." Is this the intended meaning ? There is an additional description in the devguide: "python-dev is the primary mailing list for discussions about Python’s development. The list is open to the public and is subscribed to by all core developers plus many people simply interested in following Python’s development. Discussion is focused on issues related to Python’s development, such as how to handle a specific issue, a PEP, etc." I would interpret it as "it's OK for non core developers to post", except for the "many people simply interested in following Python’s development" part which hints that we are welcome to "follow" but not actively participate. So in either case (and both descriptions) it would IMHO be useful to use clearer language. Another point: later in the Mailman description, one reads: "Consider using Gmane." (with a link to http://dir.gmane.org/gmane.comp.python.devel ). I went to Gmane and found all the links broken. I remember there was some turmoil at Gmane a couple of years back: https://en.wikipedia.org/wiki/Gmane#References => It seems the migration didn't work that well and the service shouldn't been relied upon. Regards, S. -- Stefane Fermigier - http://fermigier.com/ - http://twitter.com/sfermigier - http://linkedin.com/in/sfermigier Founder & CEO, Abilian - Enterprise Social Software - http://www.abilian.com/ Chairman, Free Group @ Systematic Cluster - http://www.gt-logiciel-libre.org/ Co-Chairman, National Council for Free & Open Source Software (CNLL) - http://cnll.fr/ Founder & Organiser, PyParis & PyData Paris - http://pyparis.org/ & http://pydata.fr/ ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] The new and improved PEP 572, same great taste with 75% less complexity!
On Tue, Apr 24, 2018 at 11:55:16PM -0700, Nathaniel Smith wrote: > These examples all make me very nervous. The order of execution in > comprehensions is pretty confusing to start with (right to left, > except when it's left to right!). I don't think comprehensions are ever right to left. With one small irregularity, execution follows Python's normal order, namely (mostly) left to right except where precedence requires something different, the if operator etc. The one small irregularity is that the values produced by the comprehension are written first, rather than last. That is, given: [expression for item in iterable ...] the initial expression doesn't get evaluated until the loop is entered. But the expression still evaluates in left-to-write order (modulo operator precedence etc) and everything following the first "for" keywords also evaluates in left-to-right order. [...] > Concretely, I find it unnerving that two of these work, and one doesn't: > > # assignment on the right of usage > results = [(x, y, x/y) for x in input_data if (y := f(x)) > 0] This shouldn't be surprising. We place the expression first because that's the most important part of the comprehension, the values it generates. Everything from the "for" onwards is just scaffolding needed to produce those values, its the leading expression that matters. But clearly we can't expect to evaluate the expression *before* the loop, even though we want it written first. So although the comprehension is written expression first, the expression is actually evaluated *last*. results = [ for x in input_data if (y := f(x)) > 0 (x, y, x/y) ] So when reading a comprehension in execution order: - look ahead to the first "for" keyword; - read to the end of the comprehension, using normal left-to-right execution order (modulo the usual exceptions); - jump back to the expression at the start; - read in normal left-to-right execution order. So aside from that small anomaly of the expression coming first, comprehensions use the same order as regular Python code. Binding expressions won't change that. > I could probably figure it out if necessary, but even in > simple cases like f(g(), h()), then do you really know for certain off > the top of your head whether g() or h() runs first? Of course. Left-to-right execution order (modulo the usual...) is a language guarantee. > Does the average user? I dare say the average user probably doesn't even think about it, and merely assumes that everything is left to right until they learn differently. In this case, that reasonable default position is correct. Everything is left to right until you learn differently. > With code like f(a := g(), h(a)) this suddenly matters a lot! That's hardly different from any other precedence issue, and even when precedence is specified by the language, sometimes adding a few extra brackets makes things clearer even when they're not needed: func(arg, (spam*n == token), (a := g()), h(a)) > But comprehensions suffer from a particularly extreme version of this, > so it worries me that they're being put forward as the first set of > motivating examples. Ha, I think that comprehensions are one of the weaker motivating examples, but it was a discussion on Python-Ideas about adding special syntax *only* to comprehensions which lead to Chris writing the PEP. So it is purely an accident of history why comprehensions are the first example in the PEP. > > # Capturing regular expression match objects > > # See, for instance, Lib/pydoc.py, which uses a multiline spelling > > # of this effect > > if match := re.search(pat, text): > > print("Found:", match.group(0)) > > Now this is a genuinely compelling example! re match objects are > always awkward to work with. But this feels like a very big hammer to > make re.match easier to use :-). I wonder if there's anything more > focused we could do here? A reasonable point. [...] > However, I do think it'd be kinda confusing if we had: > > if EXPR as X: > while EXPR as X: > with EXPR as X: > > and the first two assign the value of EXPR to X, while the last one > does something more subtle. Or maybe it'd be fine? It would be fine, right up to the point that it wasn't, and then it would be a brain melting bug magnet that would cause programmers to curse us onto the 20th generation. -- Steve ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 572: Write vs Read, Understand and Control Flow
On Wed, Apr 25, 2018 at 4:56 AM, Tim Peterswrote: > [Tim] > >> Binding expressions are debugger-friendly in that they _don't_ just > >> vanish without a trace. It's their purpose to _capture_ the values of > >> the expressions they name. Indeed, you may want to add them all over > >> the place inside expressions, never intending to use the names, just > >> so that you can see otherwise-ephemeral intra-expression results in > >> your debugger ;-) > > > [Steven D'Aprano ] > wrote: > > That's a fantastic point and I'm surprised nobody has thought of it > > until now (that I've seen). > > > > Chris, if you're still reading this and aren't yet heartedly sick and > > tired of the PEP *wink* this ought to go in as another motivating point. > > You know, I thought I was joking when I wrote that - but after I sent > it I realized I wasn't ;-) > > You just don't realise how perspicacious you truly are, Tim! > It would actually be quite convenient, and far less error-prone, to > add a binding construct inside a complicated expression for purposes > of running under a debugger. The alternative is typing the > sub-expression(s) of interest by hand at the debugger prompt, or > adding print()s, both of which are prone to introducing typos, or > changing results radically due to triggering side effects in the code > invoked by the duplicated sub-expression(s). Adding a binding > construct wouldn't change anything about how the code worked (apart > from possibly clobbering a local name). Indeed, in the cases where I currently find myself unwrapping expressions to capture their values in local variables for debugging purposes it would usually be far less intrusive to bind a name to the expression inline, then use the debugger to inspect the value. ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 572: Write vs Read, Understand and Control Flow
On 4/24/2018 6:10 PM, Tim Peters wrote: Luckily, I only have to write code for me now, so am free to pick the perfect compromise in every case;-) QOTD ! I'm in the same situation. ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] assignment expressions: an alternative alternative proposal
On Wed, Apr 25, 2018 at 6:16 AM, Steven D'Apranowrote: > On Tue, Apr 24, 2018 at 03:54:30PM -0700, Guido van Rossum wrote: > > > We should really take this back to python-ideas at this point. > > Please no :-( > +1 ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 572: Assignment Expressions
On Wed, Apr 25, 2018 at 6:15 AM, Nick Coghlanwrote: > On 25 April 2018 at 13:56, Guido van Rossum wrote: > > On Tue, Apr 24, 2018 at 8:24 PM, Nick Coghlan > wrote: > >> > >> I also think it would be good for the PEP to spell out the following > >> semantic invariant for code running in a regular namespace: > >> > >> _rhs = expr > >> assert (target := _rhs) is _rhs and target is _rhs > >> > >> It's the restriction to single names as targets that makes it possible > >> to impose such a strong assertion about what the syntax means. > > > > Can you elaborate? I don't understand what you mean by this. > > The assertion is just spelling out in code form that given the name > binding expression "target := expr", then: > > 1. the result of the expression itself is "expr", exactly as if the > name binding was omitted > 2. that result is also bound to the name "target" in the current scope > > The preceding "_rhs = expr" line is included to make the invariant > generalise even to expressions that have side effects or can change > their output based on mutable system state. > > Ironically, that may be clearer if I use another assignment statement > to break it out as two separate invariants: > > _rhs = expr > _inline_result = (bound_name := _rhs) > assert _inline_result is _rhs > assert bound_name is _rhs > > By contrast, the protocols involved in handling more complex > assignment targets and in handling augmented assignment mean that it > wouldn't be possible to define a similarly simple invariant of what > they mean (since the exact runtime behaviour would be both type and > target dependent). > > While it's handy that one _could_ use any valid assignment target, allowing this wouldn't (IMHO) necessarily be a good idea. Binding/assignment expressions fit well into Python's semantics (where bindings copy references rather than data) precisely because names are effectively and straightforwardly shorthand for values at the point of assignment. Restricting the targets in target := expression to simple names would avoid a lot of the tricksiness that less experienced programmers might be tempter to indulge, leading to simpler code without undue restrictions on what can be done. The PEP is currently somewhat confused on naming, since it is entitled "Assignment Expressions" but appears to then exclusively use the name "named expressions" to reference the concept under "Syntax and Semantics" and "assignment expression" elsewhere. I'd prefer the term "name binding expressions," since that implies the stricture that more complex targets are excluded. Whatever is chosen, usage in the PEP should be consistent. regards Steve ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] The new and improved PEP 572, same great taste with 75% less complexity!
On Wed, Apr 25, 2018 at 4:55 PM, Nathaniel Smithwrote: > On Tue, Apr 24, 2018 at 8:31 AM, Chris Angelico wrote: >> The most notable change since last posting is that the assignment >> target is no longer as flexible as with the statement form of >> assignment, but is restricted to a simple name. >> >> Note that the reference implementation has not been updated. > > I haven't read most of the discussion around this, so my apologies if > I say anything that's redundant. But since it seems like this might be > starting to converge, I just read through it for the first time, and > have a few comments. > > First, though, let me say that this is a really nice document, and I > appreciate the incredible amount of work it takes to write something > like this and manage the discussions! Regardless of the final outcome > it's definitely a valuable contribution. Thank you, but I'm hoping to do more than just rejected PEPs. (I do have one co-authorship to my name, but that was something Guido started, so it kinda had a bit of an advantage there.) My reputation as champion of death march PEPs is not something I want to continue. :| > Concretely, I find it unnerving that two of these work, and one doesn't: > > # assignment on the right of usage > results = [(x, y, x/y) for x in input_data if (y := f(x)) > 0] > > # assignment on the left of usage > stuff = [[y := f(x), x/y] for x in range(5)] > > # assignment on the right of usage > stuff = [[x/y, y := f(x)] for x in range(5)] Fair point. However, this isn't because of assignment operators, but because of comprehensions. There are two important constructs in Python that have out-of-order evaluation: x if cond else y # evaluates cond before x [result for x in iterable if cond] # evaluates result last target()[0] = value # evaluates target after value ... Amongst our weaponry ... err, I'll come in again. Ahem. Most of Python is evaluated left-to-right, top-to-bottom, just as anyone familiar with a Latin-derived language would expect. There are exceptions, however, and those are generally on the basis that "practicality beats purity". Those exceptions include assignment, the if/else expression, and comprehensions/genexps. But even within those constructs, evaluation is left-to-right as much as it possibly can be. A list comprehension places the target expression first (exception to the LTR rule), but evaluates all its 'for' and 'if' clauses in order. We *already* have some strange cases as a result of this out-of-order evaluation. For instance: reciprocals = [1/x for x in values if x] The 'if x' on the right means we won't get a ZeroDivisionError from the expression on the left. Were this loop to be unrolled, it would look like this: def listcomp(): result = [] for x in values: if x: result.append(1/x) return result reciprocals = listcomp() And you can easily audit the longhand form to confirm that, yes, "if x" comes before "1/x". It's the same with assignment expressions; the only exception to the "left before right" rule is the primary expression being evaluated after all for/if clauses. Unrolling your three examples gives (eliding the function wrappers for simplicity): # assignment on the right of usage for x in input_data: if (y := f(x)) > 0: results.append((x, y, x/y)) # assignment on the left of usage for x in range(5): stuff.append([y := f(x), x/y]) # assignment on the right of usage for x in range(5): stuff.append([x/y, y := f(x)]) Were list comprehensions *wrong* to put the expression first? I'm not sure, but I can't see a better way to write them; at best, you'd end up with something like: [for x in numbers if x % 2: x * x] which introduces its own sources of confusion (though I have to say, it would look pretty clean in the "one for loop, no conditions" case). But whether they're right or wrong, they're what we have, and side effects are already legal, and assignment expressions are just another form of side effect. > I guess this isn't limited to comprehensions either – I rarely see > complex expressions with side-effects embedded in the middle, so I'm > actually a bit hazy about the exact order of operations inside Python > expressions. I could probably figure it out if necessary, but even in > simple cases like f(g(), h()), then do you really know for certain off > the top of your head whether g() or h() runs first? Does the average > user? With code like f(a := g(), h(a)) this suddenly matters a lot! > But comprehensions suffer from a particularly extreme version of this, > so it worries me that they're being put forward as the first set of > motivating examples. Honestly, I would fully expect that g() is run first, but I know there are more complicated cases. For instance, here are three ways to print "1" followed by "2", and create a dictionary mapping None to None: >>> x={} >>> x[print(2)] = print(1) 1 2 >>> x={print(1): print(2)} 1 2 >>>
Re: [Python-Dev] The new and improved PEP 572, same great taste with 75% less complexity!
On Tue, Apr 24, 2018 at 8:31 AM, Chris Angelicowrote: > The most notable change since last posting is that the assignment > target is no longer as flexible as with the statement form of > assignment, but is restricted to a simple name. > > Note that the reference implementation has not been updated. I haven't read most of the discussion around this, so my apologies if I say anything that's redundant. But since it seems like this might be starting to converge, I just read through it for the first time, and have a few comments. First, though, let me say that this is a really nice document, and I appreciate the incredible amount of work it takes to write something like this and manage the discussions! Regardless of the final outcome it's definitely a valuable contribution. > Recommended use-cases > = > > Simplifying list comprehensions > --- > > A list comprehension can map and filter efficiently by capturing > the condition:: > > results = [(x, y, x/y) for x in input_data if (y := f(x)) > 0] > > Similarly, a subexpression can be reused within the main expression, by > giving it a name on first use:: > > stuff = [[y := f(x), x/y] for x in range(5)] > > # There are a number of less obvious ways to spell this in current > # versions of Python, such as: > > # Inline helper function > stuff = [(lambda y: [y,x/y])(f(x)) for x in range(5)] > > # Extra 'for' loop - potentially could be optimized internally > stuff = [[y, x/y] for x in range(5) for y in [f(x)]] > > # Using a mutable cache object (various forms possible) > c = {} > stuff = [[c.update(y=f(x)) or c['y'], x/c['y']] for x in range(5)] > > In all cases, the name is local to the comprehension; like iteration > variables, > it cannot leak out into the surrounding context. These examples all make me very nervous. The order of execution in comprehensions is pretty confusing to start with (right to left, except when it's left to right!). But usually this is fine, because comprehensions are mostly used in a functional/declative-ish style, where the exact order doesn't matter. (As befits their functional language heritage.) But := is a side-effecting operator, so when you start using it here, I suddenly have to become extremely aware of the exact order of execution. Concretely, I find it unnerving that two of these work, and one doesn't: # assignment on the right of usage results = [(x, y, x/y) for x in input_data if (y := f(x)) > 0] # assignment on the left of usage stuff = [[y := f(x), x/y] for x in range(5)] # assignment on the right of usage stuff = [[x/y, y := f(x)] for x in range(5)] I guess this isn't limited to comprehensions either – I rarely see complex expressions with side-effects embedded in the middle, so I'm actually a bit hazy about the exact order of operations inside Python expressions. I could probably figure it out if necessary, but even in simple cases like f(g(), h()), then do you really know for certain off the top of your head whether g() or h() runs first? Does the average user? With code like f(a := g(), h(a)) this suddenly matters a lot! But comprehensions suffer from a particularly extreme version of this, so it worries me that they're being put forward as the first set of motivating examples. > Capturing condition values > -- Note to Chris: your examples in this section have gotten their order scrambled; you'll want to fix that :-). And I'm going to reorder them yet again in my reply... > # Reading socket data until an empty string is returned > while data := sock.read(): > print("Received data:", data) I don't find this example very convincing. If it were written: for data in iter(sock.read, b""): ... then that would make it clearer what's happening ("oh right, sock.read uses b"" as a sentinel to indicate EOF). And the fact that this is needed at all is only because sockets are a low-level API with lots of complexity inherited from BSD sockets. If this were a normal python API, it'd just be for data in sock: ... (Hmm, I guess the original example is actually wrong because it should be sock.recv, and recv takes a mandatory argument. To be fair, adding that argument would also make the iter() version uglier, and that argument explains why we can't support 'for data in sock'. But this is still consistent with my argument that working directly with sockets is always going to be a bit awkward... I don't think bits of sugar like this are going to make any substantive difference to how easy it is read or write raw socket code.) > # Proposed syntax > while (command := input("> ")) != "quit": > print("You entered:", command) > > # Equivalent in current Python, not caring about function return value > while input("> ") != "quit": > print("You entered a command.") > > # To capture the return value in current Python
Re: [Python-Dev] PEP 572: Write vs Read, Understand and Control Flow
On 4/24/2018 8:56 PM, Tim Peters wrote: The alternative is typing the sub-expression(s) of interest by hand at the debugger prompt, or adding print()s, both of which are prone to introducing typos, or changing results radically due to triggering side effects in the code invoked by the duplicated sub-expression(s). Adding a binding construct wouldn't change anything about how the code worked (apart from possibly clobbering a local name). I've done both subexpression mangling (attempts at duplication) and added print statements and experienced these negative side effects, taking twice as long (or more) as intended to get the debugging information needed. While I'm no core developer, and would have a mild appreciation of avoiding those while True: loops so was generally in favor of this PEP, but not enough to be inpsired to speak up about it, I would frequently benefit from this capability... adding extra binding names, and printing _them_ instead of the duplicated subexpressions. +1 Glenn ___ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com