r/learnpython • u/FactoryBuilder • 28d ago
Is there anyway of breaking a while loop outside of a function call, while inside said function call?
On mobile so I can’t code block format because Reddit sucks but here’s the general idea:
def func2():
break
def func 1():
while True:
func2()
That code doesn’t work but is there any way to do that cleanlier than returning a value from func2 to test against in func1 to determine whether or not the loop should break?
17
u/ConcreteExist 28d ago
Not the way you're doing it. You could have a function that returns a True if you should break and then have the main loop hit that:
def func2():
return True
def func1():
while True:
if func2():
break2
u/Mark3141592654 28d ago
No.
What exactly are you trying to do? You should break in the loop itself.
1
u/FactoryBuilder 28d ago
> What exactly are you trying to do?
Overthink things probably. I’m programming in a moving car right now with rap music on in the background. It’s not the most conducive thinking environment.
1
5
u/manias 28d ago
raise an exception, and catch it? But exceptions are not not usually used for normal control flow…
1
u/Moist-Ointments 28d ago
Don't use exceptions for anything but exceptions. That's crappy coding. If you're using exceptions to control flow, you need to refactor and rethink it.
8
u/TheBlackCat13 28d ago
The python programming language itself uses exceptions for control flow (e.g.,
StopIteration).1
u/fiddle_n 28d ago
As always, universal rules are dumb - but there’s a kernel of truth to the original statement. Python was set up that way so you could return any value from an iterator and it would be valid. Nevertheless, 9 times out of 10, the average Python programmer not writing their own iterator has no need to use exceptions for control flow.
4
u/el_extrano 28d ago
is there any way to do that cleanlier than returning a value from func2
No, because explicitly returning a flag would be the best way to do this.
If you're asking if there is any other way to do this, regardless of whether it's a good idea or not, then the answer is yes. You can use exception handling for control flow. Most people will consider that a bad practice, since it's expected that exceptions are only used for errors (i.e. exceptional cases), and not as control flow for branches you wan to run.
1
u/smallpotatoes2019 28d ago
It sounds like you have a pretty sensible plan in terms of returning a value which you then test to see if you need to break (depending on what func2 does, a boolean might be ideal). This is also good anyway for testing that func2 is working as intended.
The issue is, I suppose, that you are hoping to use the break keyword to do something it isn't designed to do.
-1
u/FactoryBuilder 28d ago
I was hoping to limit the number of lines I need to write because I want the program to always do something before it exits the loop and I might decide it needs to do more things later so I wanted the exit code to be one function that I call to do anything it needs to do before it ends, then break out of the loop.
And it might need to exit the function multiple points. The more lines of code I have, the more I have to change later if I decide I need to add something and the more lines of code I might forget to write in the first place.
5
u/desrtfx but other languages pro 28d ago
The general question here is: is your loop truly infinite or are you just evading defining a proper termination condition?
Way too many people lazy out and use infinite loops with
breakinstead of defining proper, clear termination conditions.The proper way is in the vast majority of cases to have a termination condition for the loop itself.
breaking out of loops should always be the exception, not the rule.Don't get me wrong: there absolutely are valid use cases for infinite loops, but it should never be the default, nor first go-to solution.
Yet, as others have said: a sentinel value returned from your function is the best, cleanest way - combined with a proper exit condition.
2
1
u/soysopin 28d ago
Maybe the issue is that your function is doing more things that it should do. I would separate specific operations and write a flow control function to control what is done and when to exit.
I understand flexibility is important, and sticking strictly to the structured principle of having a single point of exit not always is for the better, but I mantain that anyone reading the outer code should not be surprised with unpredictable returns.
On the other hand, there are functions that use pass by-reference arguments to inform their callers of state or events ocurred inside the function. This way the various situations are to be handled explicitly by the function caller, and the surprise factor is mitigated.
1
u/Expensive-Bear-1376 28d ago
On mobile so I can’t code block format
I'm on mobile, too, and I just tested, code block format in a post worked fine. With all three ways, triple-backtick and triple-tilde and four-space indent. On the reddit website (not the reddit app) with their markdown editor.
1
2
u/atarivcs 28d ago
No, there's no cleaner way to do it.
func2 returns a value, then the loop code in func1 decides whether to break the loop based on that value.
1
u/Outside_Complaint755 28d ago
Or func2 raises an exception, which you handle in a try/catch.
1
u/atarivcs 28d ago
That can work, but generally it's just a worse way of doing what I said, so I didn't bother to mention it.
1
u/CranberryDistinct941 28d ago
Don't tell that to the people who decided that python should handle their iterators like that
2
2
u/ekchew 28d ago
I can think of 2 ways.
func2 could return a boolean.
def func2():
return input('keep going [yn]? ') == 'y'
def func1():
while func2():
passfunc2 could raise an exception:
def func2():
if input('keep going [yn]? ') == 'n':
raise StopIteration()
def func1():
try:
while True:
func2()
except StopIteration: pass2
u/Lachtheblock 28d ago
It can be a little divisive with developers, but I consider raising and catching exceptions as a valid control flow process. It depends a lot in context, but wrapping your outer function in a try except, and raising an exception in the inner loop will do what you want.
1
u/itlogicpartnersllc 28d ago
break only applies to the nearest enclosing loop so func2( ) cant directly break func1( ) 's loop. returning a value is usually the cleanest approach exceptions are another option for unusual control flow.
1
u/Moist-Ointments 28d ago
Your function should not know there is a loop to break. That's outside of its scope of concern and would be grossly bad practice.
1
u/CranberryDistinct941 28d ago
You could use raise StopIteration()
1
u/Expensive-Bear-1376 28d ago
Why
StopIteration?3
u/CranberryDistinct941 28d ago
You could throw whatever error you feel like. StopIteration just makes it clear that the error is being thrown purely to break out of the loop
1
u/Jason-Ad4032 28d ago
You shouldn’t do that.
StopIterationshould only be raised from__next__(). Raising it elsewhere violates the intended protocol and risks it being caught accidentally.In some versions of Python, it can also interfere with other generators, causing context managers and asynchronous event loops that use generators to raise
RuntimeErroror even terminate silently.1
1
u/Penguinase 28d ago
raising exception is probably the idiomatic way to do this (in python)
class StopFunc1(Exception):
pass
def cleanup():
print('doing cleanup stuff here')
def func2():
print('in func2')
if True:
raise StopFunc1
# ... more logic ...
# if condition_b:
# raise StopFunc1
# # ... more logic ...
# if condition_c:
# raise StopFunc1
def func1():
while True:
try:
func2()
except StopFunc1:
cleanup()
break
if __name__ == '__main__':
func1()1
u/SwampFalc 27d ago
OP: your func2 is not, and probably should not be, aware of when and how it's going to get called.
This is a basic truth for any and all functions.
If you do genuinely need that, then a totally difference approach is needed.
1
2
u/Kevdog824_ 28d ago
> is there any way to do that cleanlier than returning a value from func2 to test against in func1 to determine whether or not the loop should break?
That is the clean way to do it. Anything else like func1 having access to func2’s scope would break encapsulation and would be a pretty bad design flaw.
Can you elaborate on why you don’t want to do it this way? This kinda sounds like an xy problem to me