I am still learning the Python programmimg language. I asked myself in terms of code exceptions, when it is neccessary to handle such situations in a pythonic way. I read a few times "you should never pass an error silently".
For example a little function:
def square(list_with_items): return [i**2 for i in list_with_items]Is it neccessary to write an error-handler, if somebody passes a tuple as parameter?Or it is more senseful to do it, when I have to check the validation of user-input?
- 2You should catch the exception when you can handle the error. It's hard to say with your example. The question is, what would your handlerdo if you wrote one, versus writing it at a different place in the call chain?BrenBarn– BrenBarn2013-12-30 18:50:04 +00:00CommentedDec 30, 2013 at 18:50
- 2I think that what is meant by "passing an error silently" is to catch it, and not do anything useful with it (i.e. swallow it). If you don't catch it, it continues to the next level, and you can count on it not being "silent" -- someone will shout out.shx2– shx22013-12-30 18:54:31 +00:00CommentedDec 30, 2013 at 18:54
- For example the routine of my program fails somewhere. Should I print a little error message, that something at this specific point went wrong. For debbuging is this helpful, but the finaöl user is more or less interested, that function xyz got a wrong parameter.user3147268– user31472682013-12-30 18:55:29 +00:00CommentedDec 30, 2013 at 18:55
- 3Not really related to error handling, but in this example, the pythonic thing to do with a
tupleis to just handle it like you'd handle alistor generator input. Duck typing lets you do that.roippi– roippi2013-12-30 18:56:59 +00:00CommentedDec 30, 2013 at 18:56 - @shx2 Exactly that was my intention of this question.user3147268– user31472682013-12-30 18:59:19 +00:00CommentedDec 30, 2013 at 18:59
3 Answers3
In the specific case of checking types, the "Pythonic" thing to do is not to check them. Unless there is a good reason, you should assume that the caller is passing in a sensible type (note: "sensible type" might be different from "the type you expect"), and do your best to return something sensible as well. If the caller passes in a type that isn't sensible, it's perfectly acceptable to let them deal with the consequences.
For example, someone might sensibly pass an iterator ofDecimal numbers into 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 would work! But explicitly checking the types would make that use case more difficult.
And then, for example, if someone passes in something that isn't sensible, they can deal with the error:
>>> square(42)…TypeError: 'int' object isn't iterableThis error message will also (in a script) contain all the file names and line numbers necessary to debug the issue.
On the other hand, it is sometimes useful to explicitly check the arguments, when the caller might make a mistake with surprising consequences. For example, if you're writing a function which will exhibit very poor performance with alist because it expects adeque, then a check forif not isinstance(input, deque): raise TypeError("a deque must be used!") might be justified.
The name for this "method for dealing with types" is calledDuck Typing.
2 Comments
You could use an assertion:
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 could use two assertions:
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 could use an assertion and a 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 Comments
assert is for debugging only.It depends. In that specific example, if you are going to use the return value of "square" in "read-only" manner, you shouldn't have a "real bug" passing a tuple as argument. That's because even if the tuple is immutable, your function will return a new tuple with the square of the elements of the first one.
Anyhow, I suggest you to use a simple if-else statement, to be more precise and to avoid issues (i.e.: if you are going to call this function in a different way in 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 the exception with try-except statement), you could use "raise Exception" in the else-statement:
else: raise Exception('Error: type(list_with_items) must be a list!')
