7

I have a for loop that I'm needing to break if there is an error.

I want to be able to continue a for loop if a boolean is true. But don't want to have to write an "if" statement over and over again. Is it possible to call "continue" outside of a "loop"?

The following code results in an error. But is my thinking of this would work.

_Range = 6
_RangeEnd = 0

def function_to_call():
    print("x")
    if _Continue is True:
        continue

for x in range(_Range):
    _RangeEnd = _RangeEnd + 1
    function_to_call()
    if _RangeEnd == 5:
        _Continue = True

If this isn't possible. What would be an efficient way to do this? I'm reusing this function in a good number of different for loops.

TxTechnician
  • 318
  • 1
  • 11
  • Maybe to achieve what you describe you can raise some exception in the function and use try-except inside the for loop? – Roman Susi Aug 09 '22 at 19:14
  • 9
    _Is it possible to call "continue" outside of a "loop"_ No. – John Gordon Aug 09 '22 at 19:15
  • 1
    It's a little hard to tell what the best approach would be from your example code. For example, I assume you don't want to return a flag value from the inner function, but there's no indication why not in this example. – kojiro Aug 09 '22 at 19:17
  • 1
    It is best to *not call* a function in the first place if you know the input is sour. So, just put the function call inside the conditional statement. Also, you should not be relying on global variables to influence the flow of your code. – AirSquid Aug 09 '22 at 19:17
  • 1
    My intuition is that you want a [generator](https://wiki.python.org/moin/Generators). – kojiro Aug 09 '22 at 19:23
  • 1
    _"I want to be able to continue a for loop if a boolean is true"_ - did you try `while`? – TDG Aug 09 '22 at 19:25
  • 1
    How would a working version of this code look? Maybe with the "if statement over and over again"? – nneonneo Aug 09 '22 at 19:42

3 Answers3

3

You can't call continue outside of a loop, e.g. from a function called from inside a loop.

One option is to return a meaningful value from your function that tells the caller to continue, i.e. some truthy value or falsy value:

def function_that_decides_to_continue():
    print("x")
    return your_condition_here

for x in range(_Range):
    if function_that_decides_to_continue():
        continue  # skip extra stuff
    # do extra stuff

Or a sentinel object:

CONTINUE = object()  # sentinel object, whose sole purpose is to be uniquely identifiable

def function_that_decides_to_continue():
    print("x")
    if your_condition_here:
        return CONTINUE

for x in range(_Range):
    if function_that_decides_to_continue() is CONTINUE:
        continue

Another option in an error-handling use-case is directly handling those exceptions with try/except:

def function_that_may_fail(divisor):
    return 10 / divisor

for x in [2, 5, 0, 1]:
    try:
        result = function_that_may_fail(x)
    except ZeroDivisionError:
        continue

    do_stuff_with(result)

Though I admit I might be misinterpreting what you actually want to do, because perhaps the most straightforward "want to be able to continue a for loop if a boolean is true" is just a plain while:

while function_that_decides_to_continue():
    # do stuff

You'll have to provide actual examples of the "different for loops" you wanted your "function that continues" will be used in.

Kache
  • 15,647
  • 12
  • 51
  • 79
1

While I am in doubt this is a good idea for the flow control of the program, something like this can simulate what you need:

_Range = 6
_RangeEnd = 0

class Continue(Exception):
    pass

def function_to_call():
    print("x")
    if _Continue is True:
        raise Continue

for x in ...:
    try:
        function_to_call()
        something_else()
    except Continue:
        continue

And no, continue can't be outside the loop.

Roman Susi
  • 4,135
  • 2
  • 32
  • 47
  • Why are people upvoting this? Even author admits this example of using exceptions for control flow is not a good idea! – Kache Aug 10 '22 at 00:20
  • There can be rare cases, when this is the simplest way and still cleaner (and faster) than passing booleans around. – Roman Susi Aug 10 '22 at 08:01
  • 1
    Not faster execution (exceptions are comparably slow) and not faster to understand (non-idiomatic code). I'll concede that it's arguably simpler in the same way a hack can be simpler to add than it is to remove. You'll have to give an example of such "a rare case", b/c I doubt there wouldn't be a better way to do it. – Kache Aug 10 '22 at 16:02
  • The case is any "skip on error" logic. That is what exceptions are for in many languages. I am a bit lazy now to analyze whether exception from several nested (and recursive) calls are faster than returning some booleans in addition to function results. Some old experiments: https://stackoverflow.com/questions/1152541/is-it-better-to-use-an-exception-or-a-return-code-in-python – Roman Susi Aug 10 '22 at 17:54
  • OP is asking about control flow, not error handling. They are often related and used in tandem, but having [abstractions that conflate them is commonly-regarded as an anti-pattern](https://softwareengineering.stackexchange.com/questions/189222/are-exceptions-as-control-flow-considered-a-serious-antipattern-if-so-why). As an example, if the use-case is indeed error-handling, the code should `try` & `except` the underlying exception itself to avoid executing `something_else()`, not `raise ContinueException`. – Kache Aug 11 '22 at 17:47
  • Exception is just a way back to the loop from possibly nested/complex structure. If one of the main purposes of the computations in that structure is to decide whether we should skip further processing, using exceptions can make sense. If returning back is so central to the computations it will be cleaner solution even. Just try to write lets say 20 functions, which call each other in 4-tier structure and need to produce decision whether to stop computations. There will be a lot of overhead (code clutter) with passing booleans or special values around. – Roman Susi Aug 11 '22 at 19:15
  • Exception != error handling. For example, StopIteration is one more similar exception. Why not ContinueIteration :-) – Roman Susi Aug 11 '22 at 19:20
  • I challenge you to come up with an example case where you think the simplest abstraction is to have a nested function call from outside the encapsulation of an Iterator object raise a `StopIteration`/"`ContinueIteration`", so I could show you how I would refactor it to be even simpler. https://codereview.stackexchange.com/ would be good for this. – Kache Aug 11 '22 at 19:29
  • @Kache Maybe not exactly StopIteration / "ContinueIteration", but here is what I has in mind when justifying "Continue" https://codereview.stackexchange.com/questions/278825/query-builder-with-alternative-approaches That is why I consider even more use cases imaginable. – Roman Susi Aug 13 '22 at 17:55
  • ok... that question was not good enough for Code Review... – Roman Susi Aug 14 '22 at 17:58
  • I see in your own example you also found a "ContinueIteration" abstraction totally unnecessary, only needing to use normal exceptions, `try`/`except`, and `continue`. – Kache Aug 15 '22 at 19:47
  • Yes, the name "ContinueIteration" is solution-side while "NotImplementedError" - more like problem side. Used solution-side in answer here because OP may have not conveyed true meaning to decide on a better name. – Roman Susi Aug 16 '22 at 03:55
0

The continue needs to be inside a loop, I think the best solution would be to put a (if boolVar == True: continue) inside the for.