This question already has answers here:
`if key in dict` vs. `try/except` - which is more readable idiom?
(11 answers)
Closed 6 years ago.
Often, I come across cases where I find both if-else and try-except clauses can be used to solve a problem. As a result, I have some confusion deciding(and justifying) the use of a particular clause to accomplish the task at hand.
For example, let us consider a trivial situation:
In []: user = {"name": "Kshitij", "age": 20 }
# My motive is to print the user's email if it is available.
# In all other cases, a warning message needs to be printed
# Method-01 : Using try clause
In []: try:
...: print(a["email"])
...: except KeyError:
...: print("No EMAIL given")
...:
No EMAIL given
# Method-02 : Using if-else clause
In []: if "email" in a:
print(a["email"])
...: else:
...: print("No EMAIL given")
...:
No EMAIL given
I would like to know how can I decide a more Pythonic method among the two and justify it. Also, some pointers to how can one differentiate among several methods to solve similar scenarios would be really helpful.
try/catch and if/else are not interchangeable. the former is for catching errors that might be thrown. no if statement can do that. if/else is for checking if a condition is true or false. errors thrown in an if/else block will not be caught and the program will crash.
You should use exceptions if this case is an exception and not the normal case.
if/then: for internal application flow control. Errors are unlikely to be fatal
vs
try/except: for system calls and API calls where the state of the receiver is external to the code. Failures are likely to be fatal and need to handled explicitly
If you're looking for "Pythonic", neither of those is.
print a["email"] if "email" in a else "No EMAIL given"
Now that's Pythonic. But, back to the question: First of all, you decide what to do - I'm not aware of any writing conventions between those two. But, as I see it:
if-else is used mainly for detecting expectable behaviour - I mean, if "email" is not in a, it's excpectable. So we will use if-else.
An example would be your code.
But, for example, if we want to check if a string contains a numeric value, we will try to convert it to a number. If it failed, well, it's a bit harder to predict it using an if statement, so we will use try-except.
Here's a short example for that:
def is_numeric(string):
try:
a = float(string)
return True
except:
return False
of course there is a better way to check if a string is numeric, that was just an example use of try-except.
Related
This question already has answers here:
Better to 'try' something and catch the exception or test if it's possible first to avoid an exception?
(8 answers)
Closed 6 years ago.
Is a try except clause discouraged in Python if it can be avoided and is part of a normally functioning piece of code? For example, in the following two code snippets, which is preferred?
Option 1:
try:
id = list.index(42)
except:
print("42 not found")
else:
print("ID: ", id)
Option 2:
if 42 in list:
id = list.index(42)
print("ID: ", id)
else:
print("42 not found")
They are not discouraged.
However, your example is silly because you do not need an exception and you could for example accidentally have typos in your list.index code, which would trigger your exception, and you would never know.
Use exceptions and catch them if you know what exception you are catching -- and it's not trivial to do a "look before you leap" check (like it is in your example). Often that won't be the case. However, in your example it is.
Oh, and try to avoid a blanket except: catch (especially if you know how you expect your try to fail). That can easily enable bad practice since you pretty much by definition have no idea what your call might do/raise. If your application requires this (such as the server example pointed out by Paul in comments) that is fine, but if you are trying to lookup a value in a list? That's... not good.
As a good example of why this isn't good combined for your example:
try:
id = list.idex(42)
except:
print("42 not found")
else:
print("ID: ", id)
All depends on your needs:
If you have a significant dependence on the performance, try-except clauses are discouraged.
Otherwise you can use try-except clauses as a clear pattern.
Anyway, in your example I think the if-else code clearer than the try-except one.
This question already has answers here:
Best practice for using assert?
(15 answers)
Closed 4 years ago.
As suggested in the Python wiki assertions are good for
Checking parameter types, classes, or values
Checking data structure invariants
Checking "can't happen" situations (duplicates in a list, contradictory state variables)
After calling a function, to make sure that its return is reasonable
But where is the edge between using assertions and using an 'if' statement with raising exceptions next?
For example.
def some_domain_operation(user, invoice):
assert isinstance(user, (User, int))
# Do something.
vs
def some_domain_operation(user, invoice):
if not isinstance(user, (User, int)):
raise ValueError()
# Do something.
I think that using assertions is not reliable (could be disabled by the user), so I could not give a good example, when using assertions is better than using explicit an 'if' with raise next.
What is your opinion about assertions Pn python. Are they crutches?
In my view, there is a big difference between if and assert:
The expression after assert is never true [1]. If it were true, your program might as well stop executing at that point because we do not know what is true any more. [2] They are just a debugging and documentation aid for the developers who look into the source code.
Literally, an assertion says "this is true at this point of program flow. Upon no circumstance would this expression evaluate to false".
Thus, an assert should be considered an invariant. In a correctly written module, no assert will ever be hit. If we consider your case:
assert isinstance(user, (User, int))
If disabling this assert would change the behaviour of the module, it shouldn't be an assert anymore, but raise a TypeError.
[1] ... in a well-behaving, correctly written program.
[2] You should never catch AssertionError, except in the circumstances when you need to catch AssertionError
Here is the normal way of catching and throwing exceptions to callee's
def check(a):
data = {}
if not a:
raise Exception("'a' was bad")
return data
def doSomething():
try:
data = check(None)
except Exception, e:
print e
Here is an alternative + a few things I like:
'data' is always present, the 'check' function could set up some
defaults for data, the logic is then contained within the function
and doesn't have to be repeated. Also means that a dev can't make the mistake of trying to access data when an exception has occurred. (data could be defined at the very top of 'doSomething' function + assigned some defaults)
You don't have to have try/excepts everywhere cluttering up the 'doSomething' functions
def check(a):
errors = []
data = {}
if not a:
errors.append("'a' was bad")
return data, errors
def doSomething():
data, errors = check(None)
if errors:
print errors
Is there anything wrong with it? what are people's opinions?
There are times when the second approach can be useful (for instance, if you're doing a sequence of relatively independent operations and just want to keep a record of which ones failed). But if your goal is to prevent continuing in an incorrect state (i.e., not "make the mistake of trying to access data when an exception has occurred"), the second way is not good. If you need to do the check at all, you probably want to do it like this:
def check(a):
data = {}
if not a:
raise Exception("'a' was bad")
return data
def doSomething():
data = check(None)
# continue using data
That is, do the check and, if it succeeds, just keep going. You don't need to "clutter up" the code with except. You should only use try/except if you can actually handle the error in some way. If you can't, then you want the exception to continue to propagate, and if necessary, propagate all the way up and stop the program, because that will stop you from going on to use data in an invalid way.
Moreover, if it's possible to continue after the check but still "make the mistake" of accessing invalid data, then you didn't do a very good check. The point of the check, if there is one, should be to ensure that you can confidently proceed as long as the check doesn't raise an exception. The way you're doing it, you basically do the check twice: you run check, and then you check for an exception. Instead, just run the check. If it fails, raise an exception. If it succeeds, go on with your code. If you want the check to distinguish recoverable and unrecoverable errors and log the unrecoverable ones, then just do the logging in the check.
Of course, in many cases you can make it even simpler:
def doSomething():
data.blah()
# use data however you're going to use it
In other words, just do what you're going to do. You will then get an exception if you do something with data that doesn't work. There's often no reason to put in a separate explicit check. (There are certainly valid reasons for checks, of course. One would be if the actual operation is expensive and might fail at a late stage, so you want to check for validity upfront to avoid wasting time on an operation that will fail after a long computation. Another reason might be if the operation involves I/O or concurrency of some kind and could leave some shared resource in an invalid state.)
Some years from now, when you read your own code again and try to figure out what on earth you were trying to do, you'll be glad for all the clutter of the try-excepts that help make perfectly obvious what is an error and what is data.
try/excepts are not clutter; They increase the code readability by indicating that this piece of code can "expect" an exception. If you mix your logic-"ifs" with error-handling "ifs" - you may lose some readability in your code.
Further, if you know 'a' being None is the only kind of error you will have, you can write an if and handle it this way; Makes sense in this simple example you gave.
However, if a different error happens, an exception is raised anyway!
I wouldn't recommend to use it as a general programming practice to avoid try/except anywhere. It is acknowledging and marking places in code where exceptions happen.
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
This question already has answers here:
Is it better to use an exception or a return code in Python?
(6 answers)
Python: Throw Exception or return None? [closed]
(3 answers)
Closed 1 year ago.
What's better practice in a user-defined function in Python: raise an exception or return None? For example, I have a function that finds the most recent file in a folder.
def latestpdf(folder):
# list the files and sort them
try:
latest = files[-1]
except IndexError:
# Folder is empty.
return None # One possibility
raise FileNotFoundError() # Alternative
else:
return somefunc(latest) # In my case, somefunc parses the filename
Another option is leave the exception and handle it in the caller code, but I figure it's more clear to deal with a FileNotFoundError than an IndexError. Or is it bad form to re-raise an exception with a different name?
It's really a matter of semantics. What does foo = latestpdf(d) mean?
Is it perfectly reasonable that there's no latest file? Then sure, just return None.
Are you expecting to always find a latest file? Raise an exception. And yes, re-raising a more appropriate exception is fine.
If this is just a general function that's supposed to apply to any directory, I'd do the former and return None. If the directory is, e.g., meant to be a specific data directory that contains an application's known set of files, I'd raise an exception.
I would make a couple suggestions before answering your question as it may answer the question for you.
Always name your functions descriptive. latestpdf means very little to anyone but looking over your function latestpdf() gets the latest pdf. I would suggest that you name it getLatestPdfFromFolder(folder).
As soon as I did this it became clear what it should return.. If there isn't a pdf raise an exception. But wait there more..
Keep the functions clearly defined. Since it's not apparent what somefuc is supposed to do and it's not (apparently) obvious how it relates to getting the latest pdf I would suggest you move it out. This makes the code much more readable.
for folder in folders:
try:
latest = getLatestPdfFromFolder(folder)
results = somefuc(latest)
except IOError: pass
Hope this helps!
I usually prefer to handle exceptions internally (i.e. try/except inside the called function, possibly returning a None) because python is dynamically typed. In general, I consider it a judgment call one way or the other, but in a dynamically typed language, there are small factors that tip the scales in favor of not passing the exception to the caller:
Anyone calling your function is not notified of the exceptions that can be thrown. It becomes a bit of an art form to know what kind of exception you are hunting for (and generic except blocks ought to be avoided).
if val is None is a little easier than except ComplicatedCustomExceptionThatHadToBeImportedFromSomeNameSpace. Seriously, I hate having to remember to type from django.core.exceptions import ObjectDoesNotExist at the top of all my django files just to handle a really common use case. In a statically typed world, let the editor do it for you.
Honestly, though, it's always a judgment call, and the situation you're describing, where the called function receives an error it can't help, is an excellent reason to re-raise an exception that is meaningful. You have the exact right idea, but unless you're exception is going to provide more meaningful information in a stack trace than
AttributeError: 'NoneType' object has no attribute 'foo'
which, nine times out of ten, is what the caller will see if you return an unhandled None, don't bother.
(All this kind of makes me wish that python exceptions had the cause attributes by default, as in java, which lets you pass exceptions into new exceptions so that you can rethrow all you want and never lose the original source of the problem.)
with python 3.5's typing:
example function when returning None will be:
def latestpdf(folder: str) -> Union[str, None]
and when raising an exception will be:
def latestpdf(folder: str) -> str
option 2 seem more readable and pythonic
(+option to add comment to exception as stated earlier.)
In general, I'd say an exception should be thrown if something catastrophic has occured that cannot be recovered from (i.e. your function deals with some internet resource that cannot be connected to), and you should return None if your function should really return something but nothing would be appropriate to return (i.e. "None" if your function tries to match a substring in a string for example).