Analyze 'return' statement inside function - python

Sometimes inside one function we need to use the return statement several times.
When developer changes a function with multiple returns inside, it's easy to oversee some parts of code where yet another return was "hidden".
If unit tests do not cover all possible paths, disaster is guaranteed - it's only a question of time.
def my_function():
'''Multiple "return" inside'''
if condition 1:
return 1, 2, 3
if condition 2:
return 2, 3, 4
return 5 # this is wrong: only 1 value returned, while 3-tuple expected
Let's assume here: last return is wrong, because other callers expect tuple of 3 items.
I wonder if you know an easy way how to catch such parts of code automatically? I thought I could use AST, but I could not find any useful example of this.
This question is about automatic code analysis and listing such cases found - could be with running a separate script.
Of course I could write a try-to-guess parser (with e.g. regex) and then 'manually' check all unclear cases, but maybe there is a simpler way...

Depending on what version of Python you're using and what you really want to achieve, there are several ways to refactor the code.
One way, as has been already suggested, is to use Type Hints in Python 3.
Another way is refactor your code such that instead of using multiple return statements, you call other more atomic methods that handle those conditions and return the appropriate values. You use exception handling in those atomic methods to make sure the output is as desired, or raise an exception if final return type is unexpected.
def my_function():
'''Multiple "return" inside'''
if condition 1:
output = func_handle_condition_one()
if condition 2:
output = func_handle_condition_two()
output = some_other_value
if type(output) is not tuple:
raise TypeError("Invalid type for output")
return output
Additionally, ensure that you're using the right constructs for your conditions (such as whether you want to use multiple if or the if-elif-else construct). You could even re-factor your calling code to call the right function instead of calling one that has so many conditional statements.

Why not set a variable that gets returned at the end and check for its length
def my_function():
'''Multiple "return" inside'''
return_value=(0,0,0)
if condition 1:
return_value=(1, 2, 3)
elif condition 2:
return_value=(2, 3, 4)
else:
return_value=5 # this is wrong: only 1 value returned, while 3-tuple expected
try:
if len(return_value)==3:
return return_value
else:
print("Error: must return tuple of length 3")
except:
print("Error: must return tuple")

My proposal for the final type check of the result would be:
assert isinstance(return_value, tuple) and len(return_value) == 3
Advantage: as assert is easily switched off after debugging phase; still succinct statement for formulating the expectation.

Related

One liner to get valid xml strings [duplicate]

I have some a list comprehension in Python in which each iteration can throw an exception.
For instance, if I have:
eggs = (1,3,0,3,2)
[1/egg for egg in eggs]
I'll get a ZeroDivisionError exception in the 3rd element.
How can I handle this exception and continue execution of the list comprehension?
The only way I can think of is to use a helper function:
def spam(egg):
try:
return 1/egg
except ZeroDivisionError:
# handle division by zero error
# leave empty for now
pass
But this looks a bit cumbersome to me.
Is there a better way to do this in Python?
Note: This is a simple example (see "for instance" above) that I contrived because my real example requires some context. I'm not interested in avoiding divide by zero errors but in handling exceptions in a list comprehension.
I realize this question is quite old, but you can also create a general function to make this kind of thing easier:
def catch(func, handle=lambda e : e, *args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
return handle(e)
Then, in your comprehension:
eggs = (1,3,0,3,2)
[catch(lambda : 1/egg) for egg in eggs]
[1, 0, ('integer division or modulo by zero'), 0, 0]
You can of course make the default handle function whatever you want (say you'd rather return 'None' by default).
Note: in python 3, I would make the 'handle' argument keyword only, and put it at the end of the argument list. This would make actually passing arguments and such through catch much more natural.
Update (9 years later...): For Python 3, I just meant switch *args and handle, so you can specify arguments to the function without specifying handle. A minor convenience:
def catch(func, *args, handle=lambda e : e, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
return handle(e)
This is helpful when using a defined function in the comprehension:
from math import log
eggs = [1,3,0,3,2]
[catch(log, egg) for egg in eggs]
[0.0, 1.0986122886681098, ValueError('math domain error'), 1.0986122886681098, 0.6931471805599453]
Under the Python 2 version, we would've had to pass in handle before the egg.
There is no built-in expression in Python that lets you ignore an exception (or return alternate values &c in case of exceptions), so it's impossible, literally speaking, to "handle exceptions in a list comprehension" because a list comprehension is an expression containing other expression, nothing more (i.e., no statements, and only statements can catch/ignore/handle exceptions).
Function calls are expression, and the function bodies can include all the statements you want, so delegating the evaluation of the exception-prone sub-expression to a function, as you've noticed, is one feasible workaround (others, when feasible, are checks on values that might provoke exceptions, as also suggested in other answers).
The correct responses to the question "how to handle exceptions in a list comprehension" are all expressing part of all of this truth: 1) literally, i.e. lexically IN the comprehension itself, you can't; 2) practically, you delegate the job to a function or check for error prone values when that's feasible. Your repeated claim that this is not an answer is thus unfounded.
You can use
[1/egg for egg in eggs if egg != 0]
this will simply skip elements that are zero.
No there's not a better way. In a lot of cases you can use avoidance like Peter does
Your other option is to not use comprehensions
eggs = (1,3,0,3,2)
result=[]
for egg in eggs:
try:
result.append(egg/0)
except ZeroDivisionError:
# handle division by zero error
# leave empty for now
pass
Up to you to decide whether that is more cumbersome or not
I think a helper function, as suggested by the one who asks the initial question and Bryan Head as well, is good and not cumbersome at all. A single line of magic code which does all the work is just not always possible so a helper function is a perfect solution if one wants to avoid for loops. However I would modify it to this one:
# A modified version of the helper function by the Question starter
def spam(egg):
try:
return 1/egg, None
except ZeroDivisionError as err:
# handle division by zero error
return None, err
The output will be this [(1/1, None), (1/3, None), (None, ZeroDivisionError), (1/3, None), (1/2, None)]. With this answer you are in full control to continue in any way you want.
Alternative:
def spam2(egg):
try:
return 1/egg
except ZeroDivisionError:
# handle division by zero error
return ZeroDivisionError
Yes, the error is returned, not raised.
I didn't see any answer mention this.
But this example would be one way of preventing an exception from being raised for known failing cases.
eggs = (1,3,0,3,2)
[1/egg if egg > 0 else None for egg in eggs]
Output: [1, 0, None, 0, 0]
You can use generators:
def invert(xs):
for x in xs:
try:
yield x
except:
yield None
list(invert(eggs))

Does PEP8 suggest returning a variable or a function call from a method?

What is the recommended way for returning values from a method and why, according to PEP8? I tried finding documentation on this in PEP8, but couldn't find anything.
Method 1
def method():
a = meth2()
return a
Method 2
def method():
return meth2()
PEP8 doesn't specify whether or not you should return a variable versus a function.
However, it does say that you should be consistent:
Be consistent in return statements. Either all return statements in a function should return an expression, or none of them should. If any return statement returns an expression, any return statements where no value is returned should explicitly state this as return None , and an explicit return statement should be present at the end of the function (if reachable).
# Yes
def myfunction(a: int, b: int) -> int:
if a % 2 == 0:
return int(a ** b)
else:
return 0
# No
def my_bad_function(a: int, b: int) -> int:
if a % 2 == 0:
return int(a ** b)
# Implicitly returns None when the above if statement evaluates False
It's also a good idea (although not covered in PEP8) to return variables of the same type.
You'll see that I added optional type hints in the above functions. The second function will occasionally return None.
This may cause issues if another block of code which uses this function expects the return value to have the same attributes as int, such as int.bit_length()
Example of code that would result in an exception:
for n in range(1, 10):
nlen = my_bad_function(n * 5, n).bit_length()
Normally I plump for Method 1, particularly in mathematical code.
Method 1 is easier to debug and therefore maintain, since you can put a breakpoint on return a, and see easily what a is. (Wonderful as they are, the C++ Boost programmers like to adopt Method 2 with very large call stacks which can make debugging very difficult - you have to resort to inspecting a CPU register!)
Good python interpreters will have named return value optimisation, so you ought not worry about an unnecessary value copy being taken.
Method 2 seems good because there isn't any need of a variable if you are just returning the value received from the function. Plus, it looks good this way :P
Method 1 can be used for debug purposes or something else needs to be done before returning the value
I prefer method 1 bcos it makes debugging easy (can you live without it?)
If I am debugging code which is mehtod 2 then I use pycharm Evaluate Expression option to know what return statement is returning.

How to avoid for-in looping over None in Python

I know I can add some if blocks around to avoid this problem, but I am looking for a best practice or good/nice way to handle this kind of programming problem.
I am looping through the result of a function, without storing the result first in a separate variable; something like this:
for item in mycustimitemgetter.itter():
dosomething()
The itter function is returning None instead of an empty list [], and Python throws an exception per None. This is correct python behavior and nothing's wrong with it. But I'm looking for a best practice solution to:
a) keep the code clean and simple (not storing the result in a var and do if != None, etc..)
b) treat a None return value like an empty list i.e. do not run the loop and silently ignore the fact that function returned a None value.
Perhaps, use the fact that both [] and None evaluate as False, but lists with contents don't:
for item in (mycustimitemgetter.itter() or []):
dosomething()
You have a method called itter() which returns an iterable object. I would say that given the name of the function, it's reasonable to say "the returned value should always be iterable". So, if you're returning a list, you should return an empty list if you have no values.
Code there to be executed. There is nothing wrong with adding:
if retval is None:
return []
return retval
or
return retval or []
to the end of your itter() implementation, depending on the types in use.
Consider this: you wrote the code and you're facing this problem. What about someone else who's trying to use your code without knowing what you know. What do they expect?
Alternatively you should use Generators (if suitable). They contain a 'next' method (see the docs at the link), which you can return false if there are no values.

Ignore exception in Python [duplicate]

This question already has answers here:
How to properly ignore exceptions
(12 answers)
Closed 9 years ago.
I have try-except block in python, and I want to do nothing when exception occurs. My code is as follows:
for i in range(len(grid)):
for j in range(len(grid[i])):
try:
count = count > int(grid[i][j]) ? count : int(grid[i][j])
except:
//Do nothing here
How do I do nothing when exception is caught.
Thanks.
pass is the keyword you are looking for.
Let us write the code properly.
We want to iterate over each cell of the grid. So do that. Don't create lists of numbers (range) that you iterate over and then use to index back in. Python's for-loops work directly with the container. It is convenient and easy to understand and good. Don't fight that.
There is no ?: construct in Python. There is a x if y else z construct, but that is not appropriate here. You are clearly trying to set count to the maximum of its current value and the cell value. There is a built-in function for that.
You really want the maximum of all of those cells. So ask for that directly; don't assume that you have to implement the high-water-mark algorithm yourself. You don't. (This also protects you from having to pick an initial value for count, which might be wrong.) We don't need to iterate with an explicit loop for this. We can specify the list of values to pass to max with a generator expression.
You want to "ignore" values that can't be converted to integers. Notwithstanding that there probably is something wrong with your other code if the existence of such values could possibly occur in the first place: we can simply make a test, and filter out the values that fail the test.
Thus:
def is_integral(value):
try:
int(value)
return True
except:
return False
# Now, get the maximum of all the integral values:
count = max(
int(cell) for row in grid for cell in row
if is_integral(cell)
)
You can use pass, but also ... is synonymous in Python 3.x, which can be nice for writing psuedocode.
I see a lot of people use pass where they truly need to do nothing, while using ... where they are using it as a placeholder.
class SomeInterface:
def do_something(self):
pass
class SomeImplementation(SomeInterface):
def do_something(self)
...
This makes it easy to search for ... and find areas where you have unimplemented code, without the false positives from pass.
Note that passing on an exception is generally a bad practice, as you will virtually always want to act on exceptions. You should definitely, at the very least, specify the exact exception(s) you want to catch, as otherwise you will catch all exceptions, potentially causing bugs you can't detect if a different exception rears it's head.
for i in range(len(grid)):
for j in range(len(grid[i])):
try:
count = count > int(grid[i][j]) ? count : int(grid[i][j])
except:
pass
result = x if a > b else y
Is the ternary operator
The do nothing statement is
pass

How to handle exceptions in a list comprehensions?

I have some a list comprehension in Python in which each iteration can throw an exception.
For instance, if I have:
eggs = (1,3,0,3,2)
[1/egg for egg in eggs]
I'll get a ZeroDivisionError exception in the 3rd element.
How can I handle this exception and continue execution of the list comprehension?
The only way I can think of is to use a helper function:
def spam(egg):
try:
return 1/egg
except ZeroDivisionError:
# handle division by zero error
# leave empty for now
pass
But this looks a bit cumbersome to me.
Is there a better way to do this in Python?
Note: This is a simple example (see "for instance" above) that I contrived because my real example requires some context. I'm not interested in avoiding divide by zero errors but in handling exceptions in a list comprehension.
I realize this question is quite old, but you can also create a general function to make this kind of thing easier:
def catch(func, handle=lambda e : e, *args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
return handle(e)
Then, in your comprehension:
eggs = (1,3,0,3,2)
[catch(lambda : 1/egg) for egg in eggs]
[1, 0, ('integer division or modulo by zero'), 0, 0]
You can of course make the default handle function whatever you want (say you'd rather return 'None' by default).
Note: in python 3, I would make the 'handle' argument keyword only, and put it at the end of the argument list. This would make actually passing arguments and such through catch much more natural.
Update (9 years later...): For Python 3, I just meant switch *args and handle, so you can specify arguments to the function without specifying handle. A minor convenience:
def catch(func, *args, handle=lambda e : e, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
return handle(e)
This is helpful when using a defined function in the comprehension:
from math import log
eggs = [1,3,0,3,2]
[catch(log, egg) for egg in eggs]
[0.0, 1.0986122886681098, ValueError('math domain error'), 1.0986122886681098, 0.6931471805599453]
Under the Python 2 version, we would've had to pass in handle before the egg.
There is no built-in expression in Python that lets you ignore an exception (or return alternate values &c in case of exceptions), so it's impossible, literally speaking, to "handle exceptions in a list comprehension" because a list comprehension is an expression containing other expression, nothing more (i.e., no statements, and only statements can catch/ignore/handle exceptions).
Function calls are expression, and the function bodies can include all the statements you want, so delegating the evaluation of the exception-prone sub-expression to a function, as you've noticed, is one feasible workaround (others, when feasible, are checks on values that might provoke exceptions, as also suggested in other answers).
The correct responses to the question "how to handle exceptions in a list comprehension" are all expressing part of all of this truth: 1) literally, i.e. lexically IN the comprehension itself, you can't; 2) practically, you delegate the job to a function or check for error prone values when that's feasible. Your repeated claim that this is not an answer is thus unfounded.
You can use
[1/egg for egg in eggs if egg != 0]
this will simply skip elements that are zero.
No there's not a better way. In a lot of cases you can use avoidance like Peter does
Your other option is to not use comprehensions
eggs = (1,3,0,3,2)
result=[]
for egg in eggs:
try:
result.append(egg/0)
except ZeroDivisionError:
# handle division by zero error
# leave empty for now
pass
Up to you to decide whether that is more cumbersome or not
I think a helper function, as suggested by the one who asks the initial question and Bryan Head as well, is good and not cumbersome at all. A single line of magic code which does all the work is just not always possible so a helper function is a perfect solution if one wants to avoid for loops. However I would modify it to this one:
# A modified version of the helper function by the Question starter
def spam(egg):
try:
return 1/egg, None
except ZeroDivisionError as err:
# handle division by zero error
return None, err
The output will be this [(1/1, None), (1/3, None), (None, ZeroDivisionError), (1/3, None), (1/2, None)]. With this answer you are in full control to continue in any way you want.
Alternative:
def spam2(egg):
try:
return 1/egg
except ZeroDivisionError:
# handle division by zero error
return ZeroDivisionError
Yes, the error is returned, not raised.
I didn't see any answer mention this.
But this example would be one way of preventing an exception from being raised for known failing cases.
eggs = (1,3,0,3,2)
[1/egg if egg > 0 else None for egg in eggs]
Output: [1, 0, None, 0, 0]
You can use generators:
def invert(xs):
for x in xs:
try:
yield x
except:
yield None
list(invert(eggs))

Categories