What is the best practice?
In python code, error conditions are usually indicated with exceptions. You could use raise ValueError("Arrays must have the same size").
Using exception rather than return values to indicate errors has the added advantage that the exception would bubble up until it reaches a except statement. So you can defer the error handling up to a place where it makes sense. Also exceptions have an associated descriptive message instead of a magic number.
The break statement, as in many other languages, is used to interrupt the flow in loops, like ones created with while or for.
What is the best practice?
In python code, error conditions are usually indicated with exceptions. You could use raise ValueError("Arrays must have the same size").
Using exception rather than return values to indicate errors has the added advantage that the exception would bubble up until it reaches a except statement. So you can defer the error handling up to a place where it makes sense. Also exceptions have an associated descriptive message instead of a magic number.
The break statement, as in many other languages, is used to interrupt the flow in loops, like ones created with while or for.
You don't need the break
def do_some_stuff(array1, array2):
# Before doing stuff, check to ensure both arrays have the same length
if len(array1) != len(array2):
return -1
Just return the error code. In this way the rest of the code of the function will not be executed.
What does "script xyz.py returned exit code 0" mean in Python? - Stack Overflow
Error return function
Error handling in functions that returns a value
Best practice in python for return value on error vs. success - Stack Overflow
Videos
Create your own exception and raise that instead:
class MyValidationError(Exception):
pass
def my_function():
if not foo():
raise MyValidationError("Error message")
return 4
You can then call your function as:
try:
result = my_function()
except MyValidationError as exception:
# handle exception here and get error message
print exception.message
This style is called EAFP ("Easier to ask for forgiveness than permission") which means that you write the code as normal, raise exceptions when something goes wrong and handle that later:
This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C.
Raise an error:
if foo(self, input, error_msg):
raise SomethingError("You broke it")
And handle it:
try:
something()
except SomethingError as e:
print str(e)
It's the Pythonic approach and the most readable.
Returning a tuple like (12, None) may seem like a good solution, but it's hard to keep track of what each method returns if you're not consistent. Returning two different data types is even worse, as it will probably break code that assumes a constant data type.
You're looking for calls to sys.exit(...) (exit(...) calls sys.exit(...)) in the script. The argument to that method is returned to the environment as the exit code.
It's fairly likely that the script is never calling the exit(...) method, and that 0 is the default exit code.
From the documentation for sys.exit:
The optional argument arg can be an integer giving the exit status (defaulting to zero), or another type of object. If it is an integer, zero is considered “successful termination” and any nonzero value is considered “abnormal termination” by shells and the like. Most systems require it to be in the range 0-127, and produce undefined results otherwise. Some systems have a convention for assigning specific meanings to specific exit codes, but these are generally underdeveloped; Unix programs generally use 2 for command line syntax errors and 1 for all other kind of errors.
One example where exit codes are used are in shell scripts. In Bash you can check the special variable $? for the last exit status:
me@mini:~$ python -c ""; echo $?
0
me@mini:~$ python -c "import sys; sys.exit(0)"; echo $?
0
me@mini:~$ python -c "import sys; sys.exit(43)"; echo $?
43
Personally I try to use the exit codes I find in /usr/include/asm-generic/errno.h (on a Linux system), but I don't know if this is the right thing to do.
Is there a best practise for error handling in functions that returns a value?
Example
There is a function get_file_content() for opening a text document that should return the content of the file as a string.
def get_file_content(path):
return file.open(path) Now I could expand the function with a try block.
def get_file_content(path):
try:
return file.open(path)
except SomeError:
//Logging some error But what to use as a return value?
None? An empty string?
Raise the error or another error and make a second try block at the function call?
Or return some integer which is checked at the function call?
Or return a tuple with the string from the text file or an empty string and a boolean if the function have been successful?
I know that it could not be answered generally but maybe there are some best practice or real life examples.
First, whatever you do don't return a result and an error message. That's a really bad way to handle errors and will cause you endless headaches. If you need to indicate an error always raise an exception.
I usually tend to avoid raising errors unless it is necessary. In your example throwing an error is not really needed. Intersecting an empty list with a non empty one is not an error. The result is just empty list and that is correct. But let's say you want to handle other cases. For example if the method got a non-list type. In this case it is better to raise an exception. Exception are nothing to be afraid of.
My advice for you is to look at the Python library for similar functions and see how Python handles those special cases. For example have a look at the intersection method in set, it tends to be forgiving. Here I'm trying to intersect an empty set with an empty list:
>>> b = []
>>> a = set()
>>> a.intersection(b)
set([])
>>> b = [1, 2]
>>> a = set([1, 3])
>>> a.intersection(b)
set([1])
Errors are only thrown when needed:
>>> b = 1
>>> a.intersection(b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
Sure, there are cases where returning True or False on success or failure can be good. But it is very important to be consistent. The function should always return the same type or structure. It is very confusing to have a function that could return a list or a boolean. Or return the same type but the meaning of this value can be different in case of an error.
EDIT:
The OP says:
I want to return something to indicate the parameters were incorrect.
Nothing says there is an error better than an exception. If you want to indicate the parameters are incorrect then use exceptions and put a helpful error message. Returning a result in this case is just confusing. There might other cases where you want to indicate that nothing has happened but it is not an error. For example if you have a method that deletes entries from a table and the entry requested for deletion does not exist. In this case it might be fine to just return True or False on success or failure. It depends on the application and the intended behaviour
It'd be better to raise an exception than return a special value. This is exactly what exceptions were designed for, to replace error codes with a more robust and structured error-handling mechanism.
class IntersectException(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return self.msg
def intersect_two_lists(self, list1, list2):
if not list1:
raise IntersectException("list1 must not be empty.")
if not list2:
raise IntersectException("list2 must not be empty.")
#http://bytes.com/topic/python/answers/19083-standard
return filter(lambda x:x in list1,list2)
In this specific case though I'd probably just drop the tests. There's nothing wrong with intersecting empty lists, really. Also lambda is sort of discouraged these days in preference to list comprehensions. See Find intersection of two nested lists? for a couple of ways to write this without using lambda.
If you do want to raise an exception instead of returning -1 when the file doesn't exist, you could skip the check() and go directly to open() or whatever you actually want to do with the file.
The correct way to actually raise the exception is to let it get raised. So do:
def check_and_open(file):
# raises FileNotFoundError automatically
with open('xyz', 'r') as fp:
fp.readlnes() # or whatever
And if you do want to explicitly check before you open, this will raise the actual error object:
def check(file):
try:
with open(file, 'r') as fp:
# continue doing something with `fp` here or
# return `fp` to the function which wants to open file
pass
except FileNotFoundError as e:
# log error, print error, or.. etc.
raise e # and then re-raise it
Result of this version is:
>>> check('xyz')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 9, in check
File "<stdin>", line 3, in check
FileNotFoundError: [Errno 2] No such file or directory: 'xyz'
>>>
Also, note that just doing raise FileNotFoundError(file), like in another answer provided, breaks how FileNotFoundError actually raises:
Raising explicitly (the filename gets considered as the err message):
>>> raise FileNotFoundError('xyz')
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
FileNotFoundError: xyz
>>>
How it's actually raised by Python:
>>> fp = open('xyz', 'r')
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'xyz'
>>>
>>> # or with `with`:
... with open('xyz', 'r') as fp:
... fp.readlnes()
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'xyz'
>>>
You could raise an exception (FileNotFoundError is used in built-in libraries), although if you'll try to use non-existent file you'll get FileNotFoundError exception raised by default.
Then, when using this function, handle your exception:
import os
def check(file):
if not os.path.exists(file):
raise FileNotFoundError(file)
if __name__ == '__main__':
try:
check(os.path.join('foo', 'bar'))
except FileNotFoundError:
print('File was not found')
There is no such function. You couldn't build a function that does what you ask for, because by the time Python calls iserror(), the float('123') or float('as1f') expression has already been executed; if an exception is raised there, iserror() is never executed.
You'd have to delegate calling to the function:
def iserror(func, *args, **kw):
try:
func(*args, **kw)
return False
except Exception:
return True
then use it like this:
iserror(float, '123') # False
iserror(float, 'as1f') # True
Catching all errors is not that great an idea however. Although the above function tries to do the right thing by catching Exception (and thus avoids catching SystemExit or KeyboardInterrupt), it will catch MemoryError, which indicates you ran out of memory, not that the arguments to the function you tested were wrong!
Always try to catch specific exceptions; you could extend iserror() to take a specific exception:
def iserror(func, *args, **kw):
exception = kw.pop('exception', Exception)
try:
func(*args, **kw)
return False
except exception:
return True
then only catch ValueError to test your float() calls:
iserror(float, '123', exception=ValueError) # False
iserror(float, 'as1f', exception=ValueError) # True
This is no longer all that readable. I'd just stick with a simple inline try..except wherever you want to use a function call that could raise an exception, because then you can tailor your response to the specific exception without having to repeat yourself to handle the result after you determined there won't be an error:
while True:
value = raw_input('Please give a number: ')
try:
value = int(value)
break
except ValueError:
print "Sorry, {} is not a valid number, try again".format(value)
While there is no such function for the general case, there is one that solves your particular problem:
x = '123'
x.isnumeric()
returns True
while
x = 'as1f'
x.isnumeric()
returns False