Python exception handling - how to do pythonic?

I am still learning the Python programming language. I asked myself in terms of code exceptions when it is necessary to handle such situations on a pythonic path. I read several times: "You should never miss a mistake silently."

For example, a small function:

def square(list_with_items): return [i**2 for i in list_with_items] 

Do I need to write an error handler if someone passes a tuple as a parameter? Or is it more appropriate to do it when I need to check the correct user input?

+6
source share
3 answers

In the specific case, type checks of "Pythonic" need not be checked. If there is no good reason, you must assume that the caller is passing a reasonable type (note: the "reasonable type" may be different from the "type you expect"), and make every effort to return something reasonable. If the caller switches to a type that is not reasonable, it is perfectly acceptable to allow them to deal with the consequences.

For example, someone might intelligently pass a Decimal number iterator to your square function:

 >>> from decimal import Decimal >>> square(Decimal(line.strip()) for line in open("numbers.txt") [Decimal("4.0"), Decimal("9.0"), ...] 

And everything will work! But explicit type checking will make this use case more complicated.

And then, for example, if someone passes something that is unreasonable, they can handle the error:

 >>> square(42) … TypeError: 'int' object isn't iterable 

This error message also (in the script) contains all the file names and line numbers needed to debug the problem.

On the other hand, it is sometimes useful to explicitly check the arguments when the caller may make a mistake with unexpected consequences. For example, if you write a function that will demonstrate very poor performance with list because it expects deque , then checking for if not isinstance(input, deque): raise TypeError("a deque must be used!") Can be justified .

The name of this "type processing method" is called Duck Typing .

+9
source

You can use the statement:

 def square(list_with_items): assert(all([type(x) == int for x in list_with_items])) return [i**2 for i in list_with_items] 

You can use two statements:

 def square(list_with_items): assert(type(list_with_items) in (list, tuple)) assert(all([type(x) == int for x in list_with_items])) return [i**2 for i in list_with_items] 

You can use statement and try / catch:

 def square(list_with_items): assert(type(list_with_items) in (list, tuple)) try: return [i**2 for i in list_with_items] except TypeError: raise Exception("Each element of the argument must be a number.") 
-2
source

It depends. In this particular example, if you intend to use the return value of "square" in read-only mode, you should not have a "real error" passing the tuple as an argument. This is because even if the tuple is unchanged, your function will return a new tuple with the square of the elements of the first.

Anyway, I suggest you use a simple if-else statement to be more precise and avoid problems (for example: if you are going to call this function differently in the future):

 def square(list_with_items): if isinstance(list_with_items, list): # True if list_with_items is a list - False otherwise return [i**2 for i in list_with_items] else: return 'Error: type(list_with_items) must be a list!' 

EDIT: If you prefer (and if you are going to catch an exception using the try-except statement), you can use "raise Exception" in the else statement:

 else: raise Exception('Error: type(list_with_items) must be a list!') 
-2
source

All Articles