This question already has answers here:
Weird Try-Except-Else-Finally behavior with Return statements
(3 answers)
Closed 9 years ago.
There is the interesting code below:
def func1():
try:
return 1
finally:
return 2
def func2():
try:
raise ValueError()
except:
return 1
finally:
return 3
func1()
func2()
Could please somebody explain, what results will return these two functions and explain why, i.e. describe the order of the execution
From the Python documentation
A finally clause is always executed before leaving the try statement, whether an exception has occurred or not. When an exception has occurred in the try clause and has not been handled by an except clause (or it has occurred in a except or else clause), it is re-raised after the finally clause has been executed. The finally clause is also executed “on the way out” when any other clause of the try statement is left via a break, continue or return statement. A more complicated example (having except and finally clauses in the same try statement works as of Python 2.5):
So once the try/except block is left using return, which would set the return value to given - finally blocks will always execute, and should be used to free resources etc. while using there another return - overwrites the original one.
In your particular case, func1() return 2 and func2() return 3, as these are values returned in the finally blocks.
It will always go to the finally block, so it will ignore the return in the try and except. If you would have a return above the try and except, it would return that value.
def func1():
try:
return 1 # ignoring the return
finally:
return 2 # returns this return
def func2():
try:
raise ValueError()
except:
# is going to this exception block, but ignores the return because it needs to go to the finally
return 1
finally:
return 3
def func3():
return 0 # finds a return here, before the try except and finally block, so it will use this return
try:
raise ValueError()
except:
return 1
finally:
return 3
func1() # returns 2
func2() # returns 3
func3() # returns 0
Putting print statements beforehand really, really helps:
def func1():
try:
print 'try statement in func1. after this return 1'
return 1
finally:
print 'after the try statement in func1, return 2'
return 2
def func2():
try:
print 'raise a value error'
raise ValueError()
except:
print 'an error has been raised! return 1!'
return 1
finally:
print 'okay after all that let\'s return 3'
return 3
print func1()
print func2()
This returns:
try statement in func1. after this return 1
after the try statement in func1, return 2
2
raise a value error
an error has been raised! return 1!
okay after all that let's return 3
3
You'll notice that python always returns the last thing to be returned, regardless that the code "reached" return 1 in both functions.
A finally block is always run, so the last thing to be returned in the function is whatever is returned in the finally block. In func1, that's 2. In func2, that's 3.
func1() returns 2. func2() returns 3.
finally block is executed finally regardless of exception.
You can see order of execution using debugger. For example, see a screencast.
Related
I only want to return and print the error when the function is called, but it seems that the whole functions are being run when I print the function.
My expected output is only:
false
list index out of range
but I am getting:
false
false
false
list index out of range
I tried calling the function like this but did not work: print(test(error))
Question: How can I only print the error parameter and not the other parameter outside the function? Here is my code, Thanks:
def test(error=None, parameter1=None):
array = []
try:
if parameter1 == True:
print("true")
array[0]
else:
print("false")
array[0]
except Exception as e:
error = e
return error
test()
if test() is not None:
print(test())
You're running the function twice, once in the if statement, and then again in the print() statement.
If you only want to run it once, assign the result to a variable.
err = test()
if not err:
print(err)
This is happening because python is read line by line. This means that it will check the condition for parameter1 == True before going onto your statement that returns an error. Restructure the code so that it checks for an error before printing out "false". Example:
def test(error=None, parameter1=None):
array = []
try:
array[0]
except Exception as e:
error = e
return error
if parameter1 == True:
print("true")
else:
print("false")
if test() is not None:
print(test())
Additionally, the act of writing the if test() will call the function, which is why it printed false the first time. Then you called print(test()) which called it a second time, resulting in two "false"s being printed out.
I'm in the process of learning python, and I can't wrap my head around a piece of code in the book:
def find_value(List, value):
for i in range(len(List)):
if List[i] == value:
return i
return -1
I've tried running the code, and it returns the index if the value is in it, and returns -1 if it isn't, but what I don't understand is since the 'return -1' is outside the for loop, and if statement, should it not be run every time the code is executed?
Or does return only get processed once, the first time it is called?
I just want to make sure I understand this concept before moving on.
Thanks in advance
No, you can have as many return statements in as many places as you like:
def function():
... # do something
return 1
return
return [3, 4, 5]
return next(__import__('os').walk('.'))[-1:-1000000000:-1]
Though the important thing to keep in mind that only the first return that your function encounters will count, and anything else that would've succeeded it is not touched.
In the function above, you'll get 1 as the result and nothing else.
This sort of "flow of control" is why you can even do weird stuff like this -
def foo():
return True
whatIsThisIdontEven__somethingWeird.jpg # would ordinarily throw RuntimeErrors anywhere else
print(foo())
# True
In your case, it entirely depends on your code flow at runtime, but you'll still end up encountering only one return, and consequently returning only once.
Note that one difference is in the case of try-except-finally, where the return in the final clause always wins.
def function():
try:
... # do something
return 1
except SomeException:
... # do something else
finally:
return 2
In the case of normal execution, you'll encounter return 1 in try, but because of the semantics of the finally clause, you'd still end up returning 2. I believe this is the only exception.
Now, yield, on the other hand, is a different matter...
Once the function sees a return statement, the function will end and return the variable in the return statement. So the rest of the function will not be executed once the function comes across a return statement.
The return statement causes your function to exit and hand back a
value to its caller. The point of functions in general is to take in
inputs and return something.
Keep in mind : function return one at a time by memory .
So when you start the loop and then 'if' condition goes true so function return and exit.
if List[i] == value:
return i
and if you have to return many items then don't return the output instead of store the output in a list and return that list at last .
def find_value(List, value):
return_list=[]
for i in range(len(List)):
if List[i] == value:
return_list.append(i)
return return_list
In you code you wanted two return so you can try conditional return like this:
def find_value(List, value):
return_list=[]
for i in range(len(List)):
if List[i] == value:
return_list.append(i)
if return_list:
return return_list
else:
return -1
print(find_value([1,2,3,4,5,6],4))
What exactly happens, when yield and return are used in the same function in Python, like this?
def find_all(a_str, sub):
start = 0
while True:
start = a_str.find(sub, start)
if start == -1: return
yield start
start += len(sub) # use start += 1 to find overlapping matches
Is it still a generator?
Yes, it' still a generator. The return is (almost) equivalent to raising StopIteration.
PEP 255 spells it out:
Specification: Return
A generator function can also contain return statements of the form:
"return"
Note that an expression_list is not allowed on return statements in
the body of a generator (although, of course, they may appear in the
bodies of non-generator functions nested within the generator).
When a return statement is encountered, control proceeds as in any
function return, executing the appropriate finally clauses (if any
exist). Then a StopIteration exception is raised, signalling that the
iterator is exhausted. A StopIteration exception is also raised if
control flows off the end of the generator without an explict return.
Note that return means "I'm done, and have nothing interesting to
return", for both generator functions and non-generator functions.
Note that return isn't always equivalent to raising StopIteration:
the difference lies in how enclosing try/except constructs are
treated. For example,
>>> def f1():
... try:
... return
... except:
... yield 1
>>> print list(f1())
[]
because, as in any function, return simply exits, but
>>> def f2():
... try:
... raise StopIteration
... except:
... yield 42
>>> print list(f2())
[42]
because StopIteration is captured by a bare "except", as is any
exception.
Yes, it is still a generator. An empty return or return None can be used to end a generator function. It is equivalent to raising a StopIteration(see #NPE's answer for details).
Note that a return with non-None arguments is a SyntaxError in Python versions prior to 3.3.
As pointed out by #BrenBarn in comments starting from Python 3.3 the return value is now passed to StopIteration.
From PEP 380:
In a generator, the statement
return value
is semantically equivalent to
raise StopIteration(value)
There is a way to accomplish having a yield and return method in a function that allows you to return a value or generator.
It probably is not as clean as you would want but it does do what you expect.
Here's an example:
def six(how_many=None):
if how_many is None or how_many < 1:
return None # returns value
if how_many == 1:
return 6 # returns value
def iter_func():
for count in range(how_many):
yield 6
return iter_func() # returns generator
Note: you don't get StopIteration exception with the example below.
def odd(max):
n = 0
while n < max:
yield n
n = n + 1
return 'done'
for x in odd(3):
print(x)
The for loop catches it. That's its signal to stop
But you can catch it in this way:
g = odd(3)
while True:
try:
x = next(g)
print(x)
except StopIteration as e:
print("g return value:", e.value)
break
What exactly happens, when yield and return are used in the same function in Python, like this?
def find_all(a_str, sub):
start = 0
while True:
start = a_str.find(sub, start)
if start == -1: return
yield start
start += len(sub) # use start += 1 to find overlapping matches
Is it still a generator?
Yes, it' still a generator. The return is (almost) equivalent to raising StopIteration.
PEP 255 spells it out:
Specification: Return
A generator function can also contain return statements of the form:
"return"
Note that an expression_list is not allowed on return statements in
the body of a generator (although, of course, they may appear in the
bodies of non-generator functions nested within the generator).
When a return statement is encountered, control proceeds as in any
function return, executing the appropriate finally clauses (if any
exist). Then a StopIteration exception is raised, signalling that the
iterator is exhausted. A StopIteration exception is also raised if
control flows off the end of the generator without an explict return.
Note that return means "I'm done, and have nothing interesting to
return", for both generator functions and non-generator functions.
Note that return isn't always equivalent to raising StopIteration:
the difference lies in how enclosing try/except constructs are
treated. For example,
>>> def f1():
... try:
... return
... except:
... yield 1
>>> print list(f1())
[]
because, as in any function, return simply exits, but
>>> def f2():
... try:
... raise StopIteration
... except:
... yield 42
>>> print list(f2())
[42]
because StopIteration is captured by a bare "except", as is any
exception.
Yes, it is still a generator. An empty return or return None can be used to end a generator function. It is equivalent to raising a StopIteration(see #NPE's answer for details).
Note that a return with non-None arguments is a SyntaxError in Python versions prior to 3.3.
As pointed out by #BrenBarn in comments starting from Python 3.3 the return value is now passed to StopIteration.
From PEP 380:
In a generator, the statement
return value
is semantically equivalent to
raise StopIteration(value)
There is a way to accomplish having a yield and return method in a function that allows you to return a value or generator.
It probably is not as clean as you would want but it does do what you expect.
Here's an example:
def six(how_many=None):
if how_many is None or how_many < 1:
return None # returns value
if how_many == 1:
return 6 # returns value
def iter_func():
for count in range(how_many):
yield 6
return iter_func() # returns generator
Note: you don't get StopIteration exception with the example below.
def odd(max):
n = 0
while n < max:
yield n
n = n + 1
return 'done'
for x in odd(3):
print(x)
The for loop catches it. That's its signal to stop
But you can catch it in this way:
g = odd(3)
while True:
try:
x = next(g)
print(x)
except StopIteration as e:
print("g return value:", e.value)
break
So, I have this function below:
def remove_all(lst):
i = 0
while i < 10:
try:
print('Removing... ')
print(int(lst.pop()) + 10)
print("Removed successfully.")
# As soon as an IndexError is raised, jump to the following block of code...
except IndexError as err:
# if you encounter an indexerror, do the following:
print("Uh oh! Problems.")
return
#As soon as a Value error is raised, jump here.
except ValueError as err:
print("Not a number")
i = i + 1
What does the return do? There is no value after the return, so does it mean None or True?
And what is the point of having the return there if the value is none?
Thanks!
The return value is None.
In this context, the function never returns a value. The point of the return is to stop execution
The return statement can be used as a kind of control flow. By putting one (or more) return statements in the middle of a function, you could exit/stop the function.
This causes the function to exit when an IndexError is encountered, just without returning any value.
In your example, there is no return value.
Given you would call the function like this:
a = remove_all(lst)
a will be None because the function simply returns nothing.
To check the success of the function, you could implement it like this:
def ....
try:
...
exception 1:
your error handling...
return False
exception 2:
your error handling...
return False
continue function...
return True
When you then check the return value of your function, you will see if it has been executed till the end (True) or if it raised an error before (False).
However, this will not continue the execution of this particular function after one of the defined errors occurred.