Re: Loop with else clause
Possibly the final contribution:- On 9/02/19 1:30 AM, Adriaan Renting wrote: Wow, you dug deep. =Thank you. It started to 'bug' me, so I really was 'scratching an itch'! My example was the reverse of the "toy example"'s you mention as I find that often code becomes much clearer if you order it such that specific cases, sanity checking and exceptions go first, and then the default case at the end. =we have also had this discussion during code reviews. Do you think that choice/code sequence/prioritisation is related to one's facility with TDD? (some might claim it is to do with experience and/or professionalism) =My observation is that the TDD-people tend to do it 'your way', but those who 'first get it working' and then 'go looking for potential trouble' do the action-first, and exceptions-later. =presumption: that the TDD people have thought more about definitions of 'working' and 'failing' and like to implement all the 'fails' (get them 'out of the way') in order to leave the 'pure' solution. (no, let's not disappear too deeply down another 'rabbit hole'!) So my general suggestion would be to handle your empty list/no valid candidates cases first, and then do the for loop at the end. If you style this with if/elif/else, or put return statements in certain places, I have no opinion about and will depend on the specific problem. ... =I offered the three ideas to the original coder of the actual application:- Today, kicking ideas around, I coded three other possible 'solutions' for the review team's discussions: One of these involves coding three functions: the decision (yielding a boolean), the expected-case, and the unusual-case. The satisfaction in this was readability factors with a simple if statement.. Somewhat more complex, and I feel a bit OTT, was to sub-class list() and write a method which would indicate an empty list, plus include much of both the expected-case and the empty-list methods therein. Somehow "class ListShouldNotBeEmpty" doesn't seem a catchy (if descriptive) title - however, it works! The decision 'function' could also/then be made @property and (perhaps) thus contribute to readability. Lastly, I 'remembered' conditional expressions. Thus, furthering the idea of two/three functions: process_list() if list_has_contents() \ else process_empty_list() =she has grabbed the class construct and started to 'run with it'. At first I feared that I had 'created a monster'. However, once she'd improved my outline all manner of other code was filling-in the class. It turns-out that the zero-data consideration was not the only way in which this data/list varies from the standard. So, she is now monitoring how many entries are expected on the list, cf how many have been captured, and is planning to evaluate them with a sort & key=. =Thus my solution, to sub-class from list and (only) modify __getitem__(), ie the "lazy" approach, has been changed to monitor the creation of the list. She no longer uses anything obvious such as len() but is keeping track with several booleans: 1 that there is actually some data, 2 how many elements are in the list, 3 how many elements are required for the application. (perhaps someone who favored semaphores is going to say "ahah!"?) =Using the 'toy' example of the film Oscar awards: 1. is exactly as discussed 'here' 2. the traditional use of len() 3. only the top four nominees are mentioned (thus the sort, enables a 'cursor' featuring only the first four elements), before moving into the grand "and the winner is..." (the film receiving the most votes ie element[ 0 ] ) =Am almost looking forward to the follow-up code review... Many thanks (to all) for contributing ideas and advice! -- Regards =dn -- https://mail.python.org/mailman/listinfo/python-list
Re: Loop with else clause
Wow, you dug deep. My example was the reverse of the "toy example"'s you mention as I find that often code becomes much clearer if you order it such that specific cases, sanity checking and exceptions go first, and then the default case at the end. So my general suggestion would be to handle your empty list/no valid candidates cases first, and then do the for loop at the end. If you style this with if/elif/else, or put return statements in certain places, I have no opinion about and will depend on the specific problem. Cheers, Adriaan Renting. >>> On 7-2-2019 at 9:42, DL Neil wrote: > Further to our discussion of how to improve a code review's discovery of > the mistaken handling of a for...else... construct:- > > > Yesterday was a national holiday, but today gave some opportunity to > research. Way back in 2009 there was spirited discussion over on the > Python Ideas list (warning, even the mailing list's index covers > multiple screen-lengths): > > - this confusion is not new by any measure, herewith a list of previous > occasions "fists were raised concerning for..else." > https://mail.python.org/pipermail/python-ideas/2009-October/006164.html > > - an excellent summary of the 2009 debate which offers no less than six > ways to 'improve' for... else... > https://mail.python.org/pipermail/python-ideas/2009-October/006155.html > > - (as mentioned earlier) the BDFL weighed-in a couple of times. His > regret is: "That's a flaw, and I don't quite know what to do about it. > It's about 20 years too late to remove or rename it. But we probably > should not do more of these. That's a lesson." > (OK, so make that thirty years - older than the coder who the > code-review noticed falling into this coding 'gotcha'!) > https://mail.python.org/pipermail/python-ideas/2009-October/006083.html > > - herewith a (rather complicated) suggestion, and critique > https://mail.python.org/pipermail/python-ideas/2009-October/006054.html > > - one rather hopeful option (actual words to be used notwithstanding) > for i in SEQ: > A > except: > B > else: > C > appears here: > https://mail.python.org/pipermail/python-ideas/2009-October/006044.html > > > Somewhat related, PEP 548 proposed an "More Flexible Loop Control". This > was addressing the confusion caused by break within a loop. It was rejected. > > > Each of the above addresses issues 'within', that is to say happenings > during iteration - whether the entire loop or iteration cut-short by a > break (and thus the idea that "else" might be re-worded to indicate > 'after a break'). > > However, as mentioned by one contributor, the specific use-case our team > faced was an issue that arises prior to the loop. Alternately-expressed: > that according to Python's logic, prevents even a single iteration of > that loop. Thus, any 'solution' would reside outside of for and while > statements because they only consider if a loop should continue or > terminate - not handling the question of whether it should start at all! > > PEP 315 is the only discussion (I've found) which looks 'outside' or > 'before' the loop itself. It proposed an "Enhanced While Loop", > attempting to separate 'setup' or loop control from loop content. It was > rejected. > > > So, reading-around brought nothing much useful. Back to the code-face... > > Thank you to the several folk who responded with ideas to express/improve: > > if list: > process_list() #the heading and for-loop, as above > else: > print( "Sorry... > > NB this is a constructed 'toy example' attempting to be the shortest > illustration of use-cases and invented purely to communicate the need > and structure. It was expected to be interpreted as pseudo-python-code. > (you'd not use/allow "process_list" as the name of a function, would you?) > > (With apologies as necessary) one of the dangers of 'toy examples' is > the reader taking them at face value, instead of as (over-)simplified > illustrations. In 'real life' the loop code and the no-loop exception > are both considerably longer than a single line. Accordingly, using a > function would be a good way to summarise and self-document the > activity, ie the if statement's two code-blocks would make the whole > statement very/too-long (readability)! > > The "if list:" expression is overly-simplistic. The recommendation of > "if len(list):" is absolutely sound, for reasons of polymorphism. > > In-lieu of a Python construct, there are definitely situations when use > of a sentinel makes better sense. However, given their risk, in many > ways Python tries to avoid using such, eg consuming iterators until a > StopIteration exception is returned. (includes files, is subsumed by > ContextManagers...), thus "pythonic". That said, the classic use of > for... else... is in searching for a particular element within an > iterable which has all the hallmarks of a "sentinel
Re: Loop with else clause
Further to our discussion of how to improve a code review's discovery of the mistaken handling of a for...else... construct:- Yesterday was a national holiday, but today gave some opportunity to research. Way back in 2009 there was spirited discussion over on the Python Ideas list (warning, even the mailing list's index covers multiple screen-lengths): - this confusion is not new by any measure, herewith a list of previous occasions "fists were raised concerning for..else." https://mail.python.org/pipermail/python-ideas/2009-October/006164.html - an excellent summary of the 2009 debate which offers no less than six ways to 'improve' for... else... https://mail.python.org/pipermail/python-ideas/2009-October/006155.html - (as mentioned earlier) the BDFL weighed-in a couple of times. His regret is: "That's a flaw, and I don't quite know what to do about it. It's about 20 years too late to remove or rename it. But we probably should not do more of these. That's a lesson." (OK, so make that thirty years - older than the coder who the code-review noticed falling into this coding 'gotcha'!) https://mail.python.org/pipermail/python-ideas/2009-October/006083.html - herewith a (rather complicated) suggestion, and critique https://mail.python.org/pipermail/python-ideas/2009-October/006054.html - one rather hopeful option (actual words to be used notwithstanding) for i in SEQ: A except: B else: C appears here: https://mail.python.org/pipermail/python-ideas/2009-October/006044.html Somewhat related, PEP 548 proposed an "More Flexible Loop Control". This was addressing the confusion caused by break within a loop. It was rejected. Each of the above addresses issues 'within', that is to say happenings during iteration - whether the entire loop or iteration cut-short by a break (and thus the idea that "else" might be re-worded to indicate 'after a break'). However, as mentioned by one contributor, the specific use-case our team faced was an issue that arises prior to the loop. Alternately-expressed: that according to Python's logic, prevents even a single iteration of that loop. Thus, any 'solution' would reside outside of for and while statements because they only consider if a loop should continue or terminate - not handling the question of whether it should start at all! PEP 315 is the only discussion (I've found) which looks 'outside' or 'before' the loop itself. It proposed an "Enhanced While Loop", attempting to separate 'setup' or loop control from loop content. It was rejected. So, reading-around brought nothing much useful. Back to the code-face... Thank you to the several folk who responded with ideas to express/improve: if list: process_list() #the heading and for-loop, as above else: print( "Sorry... NB this is a constructed 'toy example' attempting to be the shortest illustration of use-cases and invented purely to communicate the need and structure. It was expected to be interpreted as pseudo-python-code. (you'd not use/allow "process_list" as the name of a function, would you?) (With apologies as necessary) one of the dangers of 'toy examples' is the reader taking them at face value, instead of as (over-)simplified illustrations. In 'real life' the loop code and the no-loop exception are both considerably longer than a single line. Accordingly, using a function would be a good way to summarise and self-document the activity, ie the if statement's two code-blocks would make the whole statement very/too-long (readability)! The "if list:" expression is overly-simplistic. The recommendation of "if len(list):" is absolutely sound, for reasons of polymorphism. In-lieu of a Python construct, there are definitely situations when use of a sentinel makes better sense. However, given their risk, in many ways Python tries to avoid using such, eg consuming iterators until a StopIteration exception is returned. (includes files, is subsumed by ContextManagers...), thus "pythonic". That said, the classic use of for... else... is in searching for a particular element within an iterable which has all the hallmarks of a "sentinel". Today, kicking ideas around, I coded three other possible 'solutions' for the review team's discussions: One of these involves coding three functions: the decision (yielding a boolean), the expected-case, and the unusual-case. The satisfaction in this was readability factors with a simple if statement.. Somewhat more complex, and I feel a bit OTT, was to sub-class list() and write a method which would indicate an empty list, plus include much of both the expected-case and the empty-list methods therein. Somehow "class ListShouldNotBeEmpty" doesn't seem a catchy (if descriptive) title - however, it works! The decision 'function' could also/then be made @property and (perhaps) thus contribute to readability. Lastly, I 'remembered' conditi
Re: Loop with Else Clause
-Original Message- From: Python-list On Behalf Of DL Neil Sent: Monday, February 4, 2019 11:29 PM To: 'Python' Subject: Loop with else clause What is the pythonic way to handle the situation where if a condition exists the loop should be executed, but if it does not something else should be done? -- Just reading this by itself without seeing the illustrative problem, my first gut reaction was "while loop!" but then it became more obvious that this was a one-off condition to decide to get started, as opposed to being the loop condition itself. That being said, I would agree that you really cannot do better than the if-else already presented, since it is clearly a binary choice: execute the complete loop, or don't if you can't. I personally try to avoid the else part on both while loops and for loops because of this sort of confusion. All other things being equal, having an else section is fundamentally the same thing as simply omitting the 'else:' and out-denting the consequence, and makes it very clear just what is going on. The difference, of course, appears when you have a 'break' within that loop that exits prematurely. And that's where the structured-programming purist in me starts to gibber in fear and loathing. My purist self says that if 'break' is applicable, than exception handling is just as applicable with a much cleaner model, since you can control where to go next, whether to continue repeating, etc. If I don't expect to get all the way through a for loop, I simply don't write a for loop -- I'll substitute with a while, moving the logical negation of the break condition into the while loop condition, making it very clear that I don't plan to make it to the end. Unfortunately, the for loop is such a common Pythonic thing to do that I really can't say 'for/break' is unPythonic -- I can only substitute Lovecraftian adjectives (in my opinion, of course) to describe that construct. But this whole little tangent of mine doesn't seem to apply to your situation at all, and I don't there is a 'single' language element in any programming language I can think of that does what you ask -- so stick with the cleaner: if (__): # loop; else: #don't loop Roger Christman Pennsylvania State University -- https://mail.python.org/mailman/listinfo/python-list
RE: Loop with else clause
The topic is how to deal with a python loop that may not be run if you want something else to happen in that case. Multiple solutions are presented along with this request: > Is there another, more pythonic, approach to conditional (for/while) > loop processing? Has anyone considered looking at this from the perspective of the CONDITION being evaluated? If the condition is something simple like is the "list" empty, then any method will work as it can be evaluated before trying the loop with no side effects. But what if the item in question is complex and constructed dynamically as by zipping things together or calling functions and might even produce side effects? It might be far from trivial to do that before the loop and check and you might inadvertently change things. Using a sentinel approach might be safer. And what about loops that start but exit immediately without doing anything? If you iterate on something whose first value is wrong in some way and the loop starts with an "if ... break" then do you want to do the ELSE condition because the loop sort of did not run as anticipated? A similar example is if the loop starts with a "try" and the error handler does a break. -- https://mail.python.org/mailman/listinfo/python-list
Re: Loop with else clause
I don't know if it is very Pythonic, but I would do something like if no_oscar_nominees: print ("Sorry ...") else: print_list_of_oscar_nominees() And yes, that for/else construct can be confusing. Adriaan. >>> On 5-2-2019 at 5:29, DL Neil wrote: > What is the pythonic way to handle the situation where if a condition > exists the loop should be executed, but if it does not something else > should be done? > > > Why am I asking? > Today's code review included a for...else structure. I've rarely seen > such a thing, and even knowing it exists, cannot recall ever using it! > The coder intended to implement the scenario (above) but did not realise > that the else-clause means 'execute if the loop ended without using > break'. She thought it meant 'if there's nothing in the iterable, > execute the else clause' (per if...then...else... ie the two clauses are > mutually-exclusive*) - which one assumes is the reason why the BDfL is > claimed to have said it should never have been implemented (this way). > She neglected to test the exception properly, and was lulled into a > false sense of security by the coverage reporting 100%. Oops! > > *see also the more commonly-used try...except...else...[finally...] > > > When/how does this occur? > Our client is more than a little commercially-sensitive. So as a really > simple scenario, imagine a report is required, naming people who have > become eligible for something, eg students qualified to enter an > advanced class, Oscar film award nominees, entrants who have fulfilled > the requirements of a competition from which a winner will be randomly > selected... > > The names all appear in a list, so the most frequent use-case is trivial: > > print( "And the winners are:" ) > for name in list: > print( name ) > > but, if no-one actually qualifies, a warning message is required, eg > > print( "Sorry, no-one is eligible" ) > > > Possible solution: > To make anything more than the trivial case readable, I think I'd put > the list processing into one function, and the exception into another > (except that this case is so trivial), ie > > if list: > process_list() #the heading and for-loop, as above > else: > print( "Sorry... > > > Others wanted to add a semaphore/flag inside the loop to indicate if it > was executed at least once. Yes, could even use the else clause then! > > The ideas went (rapidly) down-hill from there... > > > Is there another, more pythonic, approach to conditional (for/while) > loop processing? -- https://mail.python.org/mailman/listinfo/python-list
RE: Loop with else clause
As mentioned somewhere, "readability counts", so why not just go with exactly what you said... if len(nominees) > 0:#if a condition exists for nominee in nominees: #the loop should be executed print(nominee) else:#but if not print("D'oh! No one is worthy.") #something else should be done The fancy features of the language can be cool and all, but forcing yourself to use them just for the sake of using them and "being pythonic" can result in weird situations, and wrong or unclear assumptions or intents. When someone else reads it later it may hide what you were actually intending the check to look for. Hmm, "if nominees:" Did they make it like that to look for a 0 length list, or to make sure they didn't get None by accident, or both, or something different? Hmm, "if len(nominees) > 0:" Ahh ok, we want to make sure there's something in there. So my 2 cents: Feel free to be non-pythonic if it makes things clear and works. -Original Message- From: Python-list [mailto:python-list-bounces+david.raymond=tomtom@python.org] On Behalf Of Peter Otten Sent: Tuesday, February 05, 2019 3:44 AM To: python-list@python.org Subject: Re: Loop with else clause DL Neil wrote: > What is the pythonic way to handle the situation where if a condition > exists the loop should be executed, but if it does not something else > should be done? > Possible solution: > To make anything more than the trivial case readable, I think I'd put > the list processing into one function, and the exception into another > (except that this case is so trivial), ie > > if list: > process_list() #the heading and for-loop, as above > else: > print( "Sorry... > > > Others wanted to add a semaphore/flag inside the loop to indicate if it > was executed at least once. Yes, could even use the else clause then! An argument in favour of the flag is that it works with arbitrary iterables whereas if ...: fails: >>> numbered = enumerate([]) >>> if numbered: ... print("the winners are") ... for ix in numbered: print(*ix) ... the winners are > The ideas went (rapidly) down-hill from there... > > > Is there another, more pythonic, approach to conditional (for/while) > loop processing? I'm not aware of such an approach. -- https://mail.python.org/mailman/listinfo/python-list -- https://mail.python.org/mailman/listinfo/python-list
Re: Loop with else clause
DL Neil wrote: > What is the pythonic way to handle the situation where if a condition > exists the loop should be executed, but if it does not something else > should be done? > Possible solution: > To make anything more than the trivial case readable, I think I'd put > the list processing into one function, and the exception into another > (except that this case is so trivial), ie > > if list: > process_list() #the heading and for-loop, as above > else: > print( "Sorry... > > > Others wanted to add a semaphore/flag inside the loop to indicate if it > was executed at least once. Yes, could even use the else clause then! An argument in favour of the flag is that it works with arbitrary iterables whereas if ...: fails: >>> numbered = enumerate([]) >>> if numbered: ... print("the winners are") ... for ix in numbered: print(*ix) ... the winners are > The ideas went (rapidly) down-hill from there... > > > Is there another, more pythonic, approach to conditional (for/while) > loop processing? I'm not aware of such an approach. -- https://mail.python.org/mailman/listinfo/python-list
Re: Loop with else clause
DL Neil writes: > Possible solution: > To make anything more than the trivial case readable, I think I'd put > the list processing into one function, and the exception into another > (except that this case is so trivial), ie > > if list: > process_list() #the heading and for-loop, as above > else: > print( "Sorry... (As an aside: It's best to avoid choosing names like ‘list’ that clobber built-in names; your code examples are harder to read that way. I'll assume a different name, ‘ipsums’.) One aspect of that example I would prefer to avoid: It scatters the handling of the list to different locations in the code. It's not obvious from the purpose of ‘process_list’ whether that function should be handling an empty list; this could lead to double-handling in different locations. An alternative to consider:: if ipsums: for item in ipsums: process_item(item) else: print("Sorry...") An advantage of this is that the handling of the list is all in the same place, where changing that logic later will be easier. The ‘process_item’ then just assumes some other code has decided which items to handle; it becomes correspondingly simpler. -- \ “Two possibilities exist: Either we are alone in the Universe | `\ or we are not. Both are equally terrifying.” —Arthur C. Clarke, | _o__) 1999 | Ben Finney -- https://mail.python.org/mailman/listinfo/python-list
Re: Loop with else clause
On 5/02/19 8:12 PM, Steve wrote: Would it be a hyphythonitical question? Is that one of the new meta-classes in release 3.99, or a perhaps a project to remove the GIL and take advantage of multi-core architectures? As well as embarrassing the poor coder, this question vexed quite a few minds this afternoon (even allowing for the gathering mood - tomorrow being a public holiday!). Even though it seemed so straightforward to me, others felt it reasonable that there would be some way to tell that a loop never executed - there must be a simpler, more pythonic, construct... = Footnote: Zamboni locks up after running into large patch of loose teeth. -Original Message- From: Python-list On Behalf Of DL Neil Sent: Monday, February 4, 2019 11:29 PM To: 'Python' Subject: Loop with else clause What is the pythonic way to handle the situation where if a condition exists the loop should be executed, but if it does not something else should be done? Why am I asking? Today's code review included a for...else structure. I've rarely seen such a thing, and even knowing it exists, cannot recall ever using it! The coder intended to implement the scenario (above) but did not realise that the else-clause means 'execute if the loop ended without using break'. She thought it meant 'if there's nothing in the iterable, execute the else clause' (per if...then...else... ie the two clauses are mutually-exclusive*) - which one assumes is the reason why the BDfL is claimed to have said it should never have been implemented (this way). She neglected to test the exception properly, and was lulled into a false sense of security by the coverage reporting 100%. Oops! *see also the more commonly-used try...except...else...[finally...] When/how does this occur? Our client is more than a little commercially-sensitive. So as a really simple scenario, imagine a report is required, naming people who have become eligible for something, eg students qualified to enter an advanced class, Oscar film award nominees, entrants who have fulfilled the requirements of a competition from which a winner will be randomly selected... The names all appear in a list, so the most frequent use-case is trivial: print( "And the winners are:" ) for name in list: print( name ) but, if no-one actually qualifies, a warning message is required, eg print( "Sorry, no-one is eligible" ) Possible solution: To make anything more than the trivial case readable, I think I'd put the list processing into one function, and the exception into another (except that this case is so trivial), ie if list: process_list() #the heading and for-loop, as above else: print( "Sorry... Others wanted to add a semaphore/flag inside the loop to indicate if it was executed at least once. Yes, could even use the else clause then! The ideas went (rapidly) down-hill from there... Is there another, more pythonic, approach to conditional (for/while) loop processing? -- Regards, =dn -- https://mail.python.org/mailman/listinfo/python-list -- Regards =dn -- https://mail.python.org/mailman/listinfo/python-list
RE: Loop with else clause
Would it be a hyphythonitical question? = Footnote: Zamboni locks up after running into large patch of loose teeth. -Original Message- From: Python-list On Behalf Of DL Neil Sent: Monday, February 4, 2019 11:29 PM To: 'Python' Subject: Loop with else clause What is the pythonic way to handle the situation where if a condition exists the loop should be executed, but if it does not something else should be done? Why am I asking? Today's code review included a for...else structure. I've rarely seen such a thing, and even knowing it exists, cannot recall ever using it! The coder intended to implement the scenario (above) but did not realise that the else-clause means 'execute if the loop ended without using break'. She thought it meant 'if there's nothing in the iterable, execute the else clause' (per if...then...else... ie the two clauses are mutually-exclusive*) - which one assumes is the reason why the BDfL is claimed to have said it should never have been implemented (this way). She neglected to test the exception properly, and was lulled into a false sense of security by the coverage reporting 100%. Oops! *see also the more commonly-used try...except...else...[finally...] When/how does this occur? Our client is more than a little commercially-sensitive. So as a really simple scenario, imagine a report is required, naming people who have become eligible for something, eg students qualified to enter an advanced class, Oscar film award nominees, entrants who have fulfilled the requirements of a competition from which a winner will be randomly selected... The names all appear in a list, so the most frequent use-case is trivial: print( "And the winners are:" ) for name in list: print( name ) but, if no-one actually qualifies, a warning message is required, eg print( "Sorry, no-one is eligible" ) Possible solution: To make anything more than the trivial case readable, I think I'd put the list processing into one function, and the exception into another (except that this case is so trivial), ie if list: process_list() #the heading and for-loop, as above else: print( "Sorry... Others wanted to add a semaphore/flag inside the loop to indicate if it was executed at least once. Yes, could even use the else clause then! The ideas went (rapidly) down-hill from there... Is there another, more pythonic, approach to conditional (for/while) loop processing? -- Regards, =dn -- https://mail.python.org/mailman/listinfo/python-list -- https://mail.python.org/mailman/listinfo/python-list