This coming out of nested loops really is a generational programming problem.
The command break
, as you may know, comes out of a single for
or while
, and it has to be directly inside that for
or while
- could not be inside a function called for that purpose.
So if break were the only thing that existed, the only way to solve it would be this:
def funcao_que_resolve_se_sai():
...
return sair_ou_nao
def funcao_principal():
sair = False
for laco1 in coisas1:
for laco2 in coisas2:
...
sair = funcao_que_resolve_se_sai()
if sair:
break
if sair:
break
That is, you would have to keep the decision to leave within a variable,
and have a if
with a break
in each loop.
In Python, and other high-level languages that have the feature of "exceptions", this is solved with the use of the same.
But "exception is not only when an error occurs?" - not necessarily -
an exception may be made deliberately, with the command raise
,
precisely with the intuition of sending a signal
who manages to escape the normal order of execution.
The ideal is to create a custom exception, to differentiate it,
even if only semantically, from a real exception, caused by a
problem in execution. But to keep the example simple, I will use
the RuntimeError
even:
def funcao_que_resolve_se_sai():
...
if sair_ou_nao:
raise RuntimeError()
def funcao_principal():
try:
for laco1 in coisas1:
for laco2 in coisas2:
...
funcao_que_resolve_se_sai()
except RuntimeError:
pass
funcao_principal()
Then see how it was- when the "Runtimeerror" exception is caused by the command raise
within the call function, the program will jump straight to
the next clause except
that catch this exception. If the clavicle is after the loops we want to leave, it comes out of all the loops.
Now notice a thing a important: i put the specific function name in the except
. In your code, you put except
without anything, within their ties - this is a very bad practice, since the except
with nothing taken all error types - including errors that you are not expecting, and even more so, including any custom errors that you wanted to use to control the program flow.
So it’s very, very much so always put specific exceptions when using Excecept. In the case of your code, you use except to treat non-numeric values that the user types. The error launched in this case is the ValueError
. So your program, inside the bonds, has to capture only the Valueerror!
This technique of custom exceptions is interesting, because no matter how many loops, and how many functions are called within each other (that is - let’s say that the funcao_que_resolve_se_sai
call yet another function) - when the exception is launched, the program leaves functions and loops all, up to Except.
This gives a much greater control than calling exit
, because after Except, you continue running the program on the next line, normally. (command pass
inside Except does nothing, only leaves the block with valid syntax, because it cannot be empty)
This is used in any large Python system - for example, on web systems, frameworks always call your system functions within an appropriate "Try-except" clause - if your code causes any errors, the except
for all your code, and generates an HTML page with the appropriate error code - and, the server keeps running normally, expecting other requests.
Finally, the way to customize an exception is also very simple - just create a subclass, which can be empty, of Exception
:
class SairDosLacos(Exception):
pass
def funcao_que_resolve_se_sai():
...
if sair_ou_nao:
raise SairDosLacos()
def funcao_principal():
try:
for laco1 in coisas1:
for laco2 in coisas2:
numero_ok = False
while not numero_ok:
try:
a = int(input("digite um número"))
except ValueError:
print("número inválido, tente de novo - ")
else:
numero_ok = True
# codigo usando o número digitado:
...
funcao_que_resolve_se_sai()
except SairDosLacos:
pass
funcao_principal()
(note here the use of else
to the try/except/else
- the block else
of command try
only executed if no exception has occurred - in that case, if the number is valid)
There’s another form without exceptions, which you can’t use in your case, why don’t you put your main program links within a function - get used to always doing this.
But in addition to an exception with the raise command, another command that exits any number of loops is the return
- it terminates the current function immediately, and the control returns to the function that called the current one.
If within the function you will do nothing else after the loops (and you are within a function), it is only by a Return.
Your code would look like this:
print('Olá, seja bem vindo!')
programa = 0
while programa == 0:
def ask_continue():
ask = str(input("Deseja repetir a operação? (Y/N): "))
if ask.lower() == 'y':
program = 0
return True
elif ask.lower() == 'n':
programa =+ 1
return False
def principal():
x = 0
while x == 0:
try:
a = float(input("Informe a variável 'a': "))
b = float(input("Informe a variável 'b': "))
campo_de_saída = a*b-a+b
print("O campo de saída é {}".format(campo_de_saída))
if not ask_continue():
return
except:
print("Valor inválido, tente novamente.")
x = 0
Languages that do not have the exception mechanism, such as C, to come out of multiple loops sometimes has the command goto
- it can make the execution of the program jump unconditionally to a specific line within the same function.
The programming literature execrates the goto
so that its mention causes "fear" in many experienced professionals - because it was the only way to model ties and functions before languages that had functions as we know them today - only because of their extra flexibility, a program could get really hard to follow or understand.
Well used, however, it is the conventional way of getting out of ties - a if
in the innermost loop may contain a goto
to a point outside all ties.
I reiterate that goto
does not exist in Python - exists in C
, and is the recommended way, for example, to encode error handling if you are creating a C function that will interact, and be called from Python code.