
Hi, sometimes I do nested loops and I want to break specific loop, outer/inner etc. I need to do an ugly thing with for..else and it's annoying. It'd be nice if we could do so: for i in range(10) as loop_i: for j in range(i, i + 10) as loop_j: if i + j == 9: break loop_i or something like this. Please comment what do you think about this idea.

Greetings list, I wonder if the issue should be named as: introduce absolute break in Python as if you have 5 nested loops (which you probably should not), breaking loop_i won't do anything Kind Regards, Abdur-Rahmaan Janhangeer about <https://compileralchemy.github.io/> | blog <https://abdur-rahmaanj.github.io/> github <https://github.com/Abdur-RahmaanJ> Mauritius

On Thu, 3 Dec 2020 at 11:07, Jonatan <pybots.il@gmail.com> wrote:
Usually, in real world examples where this comes up, it's possible (and better) to refactor the inner loop into a named generator function. As a result, cases where such a feature would be useful are not as common as it initially seems. Paul

On 03.12.2020 12:06, Jonatan wrote:
While that's an interesting idea, you can already handle such "break" scenarios quite well using functions/methods and "return" instead: def work(): for i in range(10): for j in range(i, i + 10): # Do work if i + j == 9: return (i, j) print(work()) For added flexibility and "continue" scenarios you can use generators. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, Dec 03 2020)
Python Projects, Coaching and Support ... https://www.egenix.com/ Python Product Development ... https://consulting.egenix.com/
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 https://www.egenix.com/company/contact/ https://www.malemburg.com/

Hello, On Thu, 03 Dec 2020 11:06:41 -0000 "Jonatan " <pybots.il@gmail.com> wrote:
Funnily enough, this was mentioned just yesterday on a quite not directly related thread, https://github.com/WebAssembly/design/issues/796#issuecomment-737244573 . As it points out, it's PEP 3136 https://www.python.org/dev/peps/pep-3136/ , proudly bearing the badge of "Rejected". And the rejection is likely right than wrong - why patch-up language with adhoc stuff to break thru loops, if the language already offers means to break thru *absolutely any levels of anything*, including nested function calls. It's try/raise, please enjoy its usage for this purpose too, like other people do already. -- Best regards, Paul mailto:pmiscml@gmail.com

Hi Jonatan Please consider for a in aaa: for b in bbb: if condition(a, b): break KEYWORD: break where KEYWORD is like else, but with the opposite semantics. I think this does exactly what you asked for, in your example. In July this year Олег Комлев suggested this addition to the syntax and semantics, with "if break" as the KEYWORD. New clause in FOR and WHILE instead of ELSE https://mail.python.org/archives/list/python-ideas@python.org/thread/WNKNETP... I think this is less of a change to the language, and therefore more likely to be accepted, than what you proposed. To me for a in aaa: for b in bbb: if condition(a, b): break # more code here perhaps if break: break reads quite nicely. If we exited the look with a break, then please do break again. This also avoids the problem of having to remember and type label names. -- Jonathan

On Fri, Dec 4, 2020 at 12:25 AM Jonathan Fine <jfine2358@gmail.com> wrote:
well... uhhh.... Technically you can do that already.... for a in aaa: for b in bbb: if condition(a, b): break else: continue # We didn't break from b, so continue a break # We did break b, so break a But I don't think anyone's actually doing that :) ChrisA

Hi On Thu, Dec 3, 2020 at 2:02 PM Chris Angelico <rosuav@gmail.com> wrote:
This is ingenious. Thank you. Here the "else ... continue" block is for normal exit, and the suite of statements containing the break statement is for unusual exit.
But I don't think anyone's actually doing that :)
We're sort of using the 'break ... continue' as a goto, that goes to the suite after the else block. And though not in name a goto, it has the same problems as a honest goto statement. I consider your example, Chris, to be an argument in favour of allowing 'for ... if break ... else ...'. -- Jonathan

On Fri, Dec 4, 2020 at 2:42 AM Jonathan Fine <jfine2358@gmail.com> wrote:
Well, it's a toy example, so it shouldn't be considered a very strong argument. But if this IS actually being done in production, then yes, that's an argument in favour... On Fri, Dec 4, 2020 at 2:50 AM Serhiy Storchaka <storchaka@gmail.com> wrote:
I did. :(
In cases when functions or generators add significant overhead.
... and that's what Serhiy is saying. Personally, I'd be more inclined to "break 2" or "break somelabel" than a keyword for "do this if we broke out of the loop". It's not often that you actually need to say "do this if we broke out of the loop" unless the "do this" part is another break statement (otherwise, you'd just put the same code up next to the break statement); so it'd be more logical to have a way to say "break out of these two loops". ChrisA

I think this is the kind of feature that can very easily be abused. Whenever I want to break out of a nested loop I take this as an opportunity to extract the loop into its own function and use the return statement to break out of the loop. IMO this is a lot better than having named or indexed loop control because a proper function can later be reusable, and it lets you explain why you need to return early in the docstring.

For the likely rare situation where I'd want to do this rather than refactoring into a function, I might try with something like this without requiring changes to the language: from contextlib import contextmanager @contextmanager def breakable(): class Break(Exception): pass def breaker(): raise Break try: yield breaker except Break: pass This allows defining a block that can be "aborted" (which can have a loop or any other statement block within); you abort it by calling the resulting value of the context manager. An example of use is available at https://gist.github.com/dmoisset/55f5916f9f339a143b6f3d155de8706e On Thu, 3 Dec 2020 at 11:07, Jonatan <pybots.il@gmail.com> wrote:

On Mon, Dec 7, 2020 at 6:27 PM Daniel Moisset <dfmoisset@gmail.com> wrote:
I agree with the person who called this a brilliant solution. Here is the code from the link for completeness: for outer in range(5): with breakable() as brk: for middle in range(5): for inner in range(10): print(outer, middle, inner) if middle == 1 and inner == 5: brk() print(f"end middle loop #{middle}") print(f"end outer loop #{outer}") print("done") Iterating on that idea a bit and taking advantage of the walrus, what if syntax were created allowing us to remove the extra indentation level, and also allowing us to contextualize any loop without having to employ the with statement? I imagine is looking something like this: for outer in range(5): @breakable for middle in loopX := range(5): for inner in range(10): print(outer, middle, inner) if middle == 1 and inner == 5: loopX.brk() print(f"end middle loop #{middle}") print(f"end outer loop #{outer}") print("done") Above, breakable will be some decorator that returns a context manager, and the "loopX" loop happens in the context of that context manager (with the name "loopX" assigned to the context object). Here is some more detail about what I am imagining happening with this new syntax: 1. the name `loopX` is assigned to the iterable in the for statement, as usual 2. the first time the for statement ` for middle in loopX := range(5): ` is interpreted/excuted, the presence of the decorator causes this to happen before iteration starts: loopX = breakable(loopX) with loopX as loopX: for middle in loopX := range(5): # the "loopX" loop is now executed within the loopX context 3. the utility of this syntax would not be limited to the breaking of named loops. with this syntax in place, one could write any kind of context manager they want for contextualizing the execution of loops (both for loops and while loops). 4. last note: if no name is supplied to the iterable object using the walrus, then applying the decorator to the loop would be a syntax error What do you think? --- Ricky. "I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler <http://python.org/psf/codeofconduct/>

On Tue, Dec 8, 2020 at 3:31 PM Ricky Teachey <ricky@teachey.org> wrote:
I agree with the person who called this a brilliant solution. Here is the code from the link for completeness:
I'm not diss'ing the approach. But it doesn't really save that much over a sentinel variable `break_to_middle`. And if you wanted to break to any of several levels from the innermost, you'd need multiple context managers.
-- The dead increasingly dominate and strangle both the living and the not-yet born. Vampiric capital and undead corporate persons abuse the lives and control the thoughts of homo faber. Ideas, once born, become abortifacients against new conceptions.

On Tue, Dec 8, 2020 at 10:42 AM David Mertz <mertz@gnosis.cx> wrote: then the sentinel approach. My syntax suggestion came from thinking about how to make it even more readable (providing one can swallow the "this decorator automatically becomes a context manager" part-- which might be too hard to swallow I don't know). --- Ricky. "I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler

Greetings list, I wonder if the issue should be named as: introduce absolute break in Python as if you have 5 nested loops (which you probably should not), breaking loop_i won't do anything Kind Regards, Abdur-Rahmaan Janhangeer about <https://compileralchemy.github.io/> | blog <https://abdur-rahmaanj.github.io/> github <https://github.com/Abdur-RahmaanJ> Mauritius

On Thu, 3 Dec 2020 at 11:07, Jonatan <pybots.il@gmail.com> wrote:
Usually, in real world examples where this comes up, it's possible (and better) to refactor the inner loop into a named generator function. As a result, cases where such a feature would be useful are not as common as it initially seems. Paul

On 03.12.2020 12:06, Jonatan wrote:
While that's an interesting idea, you can already handle such "break" scenarios quite well using functions/methods and "return" instead: def work(): for i in range(10): for j in range(i, i + 10): # Do work if i + j == 9: return (i, j) print(work()) For added flexibility and "continue" scenarios you can use generators. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, Dec 03 2020)
Python Projects, Coaching and Support ... https://www.egenix.com/ Python Product Development ... https://consulting.egenix.com/
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 https://www.egenix.com/company/contact/ https://www.malemburg.com/

Hello, On Thu, 03 Dec 2020 11:06:41 -0000 "Jonatan " <pybots.il@gmail.com> wrote:
Funnily enough, this was mentioned just yesterday on a quite not directly related thread, https://github.com/WebAssembly/design/issues/796#issuecomment-737244573 . As it points out, it's PEP 3136 https://www.python.org/dev/peps/pep-3136/ , proudly bearing the badge of "Rejected". And the rejection is likely right than wrong - why patch-up language with adhoc stuff to break thru loops, if the language already offers means to break thru *absolutely any levels of anything*, including nested function calls. It's try/raise, please enjoy its usage for this purpose too, like other people do already. -- Best regards, Paul mailto:pmiscml@gmail.com

Hi Jonatan Please consider for a in aaa: for b in bbb: if condition(a, b): break KEYWORD: break where KEYWORD is like else, but with the opposite semantics. I think this does exactly what you asked for, in your example. In July this year Олег Комлев suggested this addition to the syntax and semantics, with "if break" as the KEYWORD. New clause in FOR and WHILE instead of ELSE https://mail.python.org/archives/list/python-ideas@python.org/thread/WNKNETP... I think this is less of a change to the language, and therefore more likely to be accepted, than what you proposed. To me for a in aaa: for b in bbb: if condition(a, b): break # more code here perhaps if break: break reads quite nicely. If we exited the look with a break, then please do break again. This also avoids the problem of having to remember and type label names. -- Jonathan

On Fri, Dec 4, 2020 at 12:25 AM Jonathan Fine <jfine2358@gmail.com> wrote:
well... uhhh.... Technically you can do that already.... for a in aaa: for b in bbb: if condition(a, b): break else: continue # We didn't break from b, so continue a break # We did break b, so break a But I don't think anyone's actually doing that :) ChrisA

Hi On Thu, Dec 3, 2020 at 2:02 PM Chris Angelico <rosuav@gmail.com> wrote:
This is ingenious. Thank you. Here the "else ... continue" block is for normal exit, and the suite of statements containing the break statement is for unusual exit.
But I don't think anyone's actually doing that :)
We're sort of using the 'break ... continue' as a goto, that goes to the suite after the else block. And though not in name a goto, it has the same problems as a honest goto statement. I consider your example, Chris, to be an argument in favour of allowing 'for ... if break ... else ...'. -- Jonathan

On Fri, Dec 4, 2020 at 2:42 AM Jonathan Fine <jfine2358@gmail.com> wrote:
Well, it's a toy example, so it shouldn't be considered a very strong argument. But if this IS actually being done in production, then yes, that's an argument in favour... On Fri, Dec 4, 2020 at 2:50 AM Serhiy Storchaka <storchaka@gmail.com> wrote:
I did. :(
In cases when functions or generators add significant overhead.
... and that's what Serhiy is saying. Personally, I'd be more inclined to "break 2" or "break somelabel" than a keyword for "do this if we broke out of the loop". It's not often that you actually need to say "do this if we broke out of the loop" unless the "do this" part is another break statement (otherwise, you'd just put the same code up next to the break statement); so it'd be more logical to have a way to say "break out of these two loops". ChrisA

I think this is the kind of feature that can very easily be abused. Whenever I want to break out of a nested loop I take this as an opportunity to extract the loop into its own function and use the return statement to break out of the loop. IMO this is a lot better than having named or indexed loop control because a proper function can later be reusable, and it lets you explain why you need to return early in the docstring.

For the likely rare situation where I'd want to do this rather than refactoring into a function, I might try with something like this without requiring changes to the language: from contextlib import contextmanager @contextmanager def breakable(): class Break(Exception): pass def breaker(): raise Break try: yield breaker except Break: pass This allows defining a block that can be "aborted" (which can have a loop or any other statement block within); you abort it by calling the resulting value of the context manager. An example of use is available at https://gist.github.com/dmoisset/55f5916f9f339a143b6f3d155de8706e On Thu, 3 Dec 2020 at 11:07, Jonatan <pybots.il@gmail.com> wrote:

On Mon, Dec 7, 2020 at 6:27 PM Daniel Moisset <dfmoisset@gmail.com> wrote:
I agree with the person who called this a brilliant solution. Here is the code from the link for completeness: for outer in range(5): with breakable() as brk: for middle in range(5): for inner in range(10): print(outer, middle, inner) if middle == 1 and inner == 5: brk() print(f"end middle loop #{middle}") print(f"end outer loop #{outer}") print("done") Iterating on that idea a bit and taking advantage of the walrus, what if syntax were created allowing us to remove the extra indentation level, and also allowing us to contextualize any loop without having to employ the with statement? I imagine is looking something like this: for outer in range(5): @breakable for middle in loopX := range(5): for inner in range(10): print(outer, middle, inner) if middle == 1 and inner == 5: loopX.brk() print(f"end middle loop #{middle}") print(f"end outer loop #{outer}") print("done") Above, breakable will be some decorator that returns a context manager, and the "loopX" loop happens in the context of that context manager (with the name "loopX" assigned to the context object). Here is some more detail about what I am imagining happening with this new syntax: 1. the name `loopX` is assigned to the iterable in the for statement, as usual 2. the first time the for statement ` for middle in loopX := range(5): ` is interpreted/excuted, the presence of the decorator causes this to happen before iteration starts: loopX = breakable(loopX) with loopX as loopX: for middle in loopX := range(5): # the "loopX" loop is now executed within the loopX context 3. the utility of this syntax would not be limited to the breaking of named loops. with this syntax in place, one could write any kind of context manager they want for contextualizing the execution of loops (both for loops and while loops). 4. last note: if no name is supplied to the iterable object using the walrus, then applying the decorator to the loop would be a syntax error What do you think? --- Ricky. "I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler <http://python.org/psf/codeofconduct/>

On Tue, Dec 8, 2020 at 3:31 PM Ricky Teachey <ricky@teachey.org> wrote:
I agree with the person who called this a brilliant solution. Here is the code from the link for completeness:
I'm not diss'ing the approach. But it doesn't really save that much over a sentinel variable `break_to_middle`. And if you wanted to break to any of several levels from the innermost, you'd need multiple context managers.
-- The dead increasingly dominate and strangle both the living and the not-yet born. Vampiric capital and undead corporate persons abuse the lives and control the thoughts of homo faber. Ideas, once born, become abortifacients against new conceptions.

On Tue, Dec 8, 2020 at 10:42 AM David Mertz <mertz@gnosis.cx> wrote: then the sentinel approach. My syntax suggestion came from thinking about how to make it even more readable (providing one can swallow the "this decorator automatically becomes a context manager" part-- which might be too hard to swallow I don't know). --- Ricky. "I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler
participants (15)
-
Abdur-Rahmaan Janhangeer
-
Chris Angelico
-
Daniel Moisset
-
David Mertz
-
Henk-Jaap Wagenaar
-
Jonatan
-
Jonathan Fine
-
M.-A. Lemburg
-
Mathew Elman
-
Paul Moore
-
Paul Sokolovsky
-
Ricky Teachey
-
Serhiy Storchaka
-
Stephen J. Turnbull
-
Valentin Berlier