Python Forum
A context to exit deeply nested loops
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
A context to exit deeply nested loops
#1
The break statement doesn't allow to exit several nested loops. It is a simple exercise to develop a block() context to do so. Here is an attempt
from contextlib import contextmanager @contextmanager def block(): """Block of code context allowing to exit deeply nested loops Usage: with block() as b: ... # somewhere in a nested loop b.exit() ... """ tok = BlockToken() try: yield tok except EndOfBlock as exc: if exc.args[0] is not tok: raise class EndOfBlock(RuntimeError): """Helper exception type for the block() context""" class BlockToken: """Helper class for block() context""" def exit(self): raise EndOfBlock(self) def func(x): """Test function for the block() context""" result = '' with block() as outer: result += 'A' for i in range(4): result += 'B' if x > 0 and i > 2: outer.exit() result += 'D' with block() as inner: result += 'C' if x == 1: outer.exit() elif x == 2: inner.exit() result += 'E' result += 'F' result += 'G' result += 'H' return result if __name__ == '__main__': assert func(0) == "ABDCEFBDCEFBDCEFBDCEFGH" assert func(1) == "ABDCH" assert func(2) == "ABDCFBDCFBDCFBH" print('SUCCESS')
An interesting feature here is that the BlockToken instance can be used as argument for functions called within the block. For example you could write
def do_something(session): if input('Do you want to quit?').strip().lower() in ('y', 'yes'): session.exit() for i in range(5): print(i) with block() as user_session: do_something(user_session) print("If we're here, the user chose to stay!")
Reply
#2
If the nested loop is in a function, return can be used instead of break to exit.
Reply
#3
Yoriz Wrote:if the nested loop is in a function, return can be used instead of break to exit.
Yes, but in this case, session.exit() exits more than the function. It exits from a block of code that contained the call to the function.
Reply
#4
In this second version, we avoid using the @contextmanager decorator. Instead, block is now a class. The advantage of this is that one can subclass block to create exitable contexts with more data, such as a user session with a user name, a user device, etc
__version__ = '2109.05.19' class block: """Block of code context allowing to exit deeply nested loops Usage: with block() as b: ... # somewhere in a nested loop b.exit() ... This type can be subclassed to create exitable contexts with useful data, for example class UserSession(block) def __init__(self, user_name): self.user_name = user_name with UserSession('Doe') as session: ... session.exit() ... """ def __enter__(self): return self def exit(self): raise EndOfBlock(self) def __exit__(self, exc_type, exc_value, ex_tb): if exc_type is EndOfBlock and exc_value.args[0] is self: # exception isn't propagated if __exit__ returns true value return True class EndOfBlock(RuntimeError): """Helper exception type for the block() context""" if __name__ == '__main__': # Example code for blocks class UserSession(block): def __init__(self, user_name): super().__init__() self.user_name = user_name def __enter__(self): print("User {}'s session starting now!".format(self.user_name)) return super().__enter__() def __exit__(self, *args): print('User {} is leaving.'.format(self.user_name)) return super().__exit__(*args) def func(x): """Test function for the block() context""" result = '' with UserSession('Doe') as outer: result += 'A' for i in range(4): result += 'B' if x > 0 and i > 2: outer.exit() result += 'D' with block() as inner: result += 'C' if x == 1: outer.exit() elif x == 2: inner.exit() result += 'E' result += 'F' result += 'G' result += 'H' return result assert func(0) == "ABDCEFBDCEFBDCEFBDCEFGH" assert func(1) == "ABDCH" assert func(2) == "ABDCFBDCFBDCFBH" print('SUCCESS')
Output:
User Doe's session starting now! User Doe is leaving. User Doe's session starting now! User Doe is leaving. User Doe's session starting now! User Doe is leaving. SUCCESS
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Deeply nested JSON editing tool w/set arithmetic epieye 0 3,636 Sep-13-2021, 06:04 PM
Last Post: epieye

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020
This forum uses Lukasz Tkacz MyBB addons.
Forum use Krzysztof "Supryk" Supryczynski addons.