Preamble
One of the concepts used in writing Python code is “Easier to ask for forgiveness than permission”, aka EAFP. Literally, this means that instead of doing checks, whether an operation is possible, you do the operation and “ask forgiveness” if what you do fails. Technically this means (just an example with no particular meaning):
try:
obj.x = 10
obj.y = 20
except AttributeError:
pass
This technique is often used when the code in try ... except
block is known to run without any errors in most cases, or if the code is rarely executed. The same code with preliminary checks would look as follows:
if hasattr(obj, 'x'):
obj.x = 10
if hasattr(obj, 'y'):
obj.y = 20
C++ practices
While generally not an issue in Python, C++ has a “heritage” flame war of “function returning error status” vs. “throwing exceptions”. This has been discussed a lot, but I can’t find any good source of information which describes whether the Python-like EAFP is applicable in Python.
E.g. consider the following:
if (node.has_element('x')) {
node['x'] = 10;
}
versus
try {
node['x'] = 10;
}
catch(ElementNotFoundException e) {
// pass
}
Which approach is generally used in C++ code? If performance is not the issue, would you write EAFP-fashioned code? Thank you.
4
It’s hard to classify something as being generally used in C++
. Not only do many companies have their own conventions, but C++ by design lets you compile without exception support at all (no support for stack unwinding, function prologue/epilogue).
That said; ignored exceptions are considered an anti-pattern in most C++ applications, and should usually be complemented by a good, documented reason. The reason not only being performance, but because it’s considered an abuse of the try/catch
mechanism. The idiom is that it should only be used for exceptional cases, not the normal flow of a program (unlike in Python, in which it is even used for iteration).
In my experience, purposely allowing an exception to be thrown for something predictable is very rare in C++ code. It’s perceived as non-performant, and despite the continually repeated mantra of no premature optimization, C++ programmers have a long culture of premature optimization.
I realize it’s just an example for illustration, but in your particular example I might actually go for a third option, which is to design the container so node['x'] = 10
would just be a no-op if the element didn’t exist, depending on how frequently this logic was needed compared to an insert. I hate repeating boilerplate code all over that can be consolidated into one place.
1
You will need to ask yourself: What are the consequences of doing something that throws an exception (often the exception isn’t the only consequence), and how hard is it to check for pre-conditions? And if you can’t prevent exceptions 100%, you still have to handle them, so you are just adding more code?
Your code
if hasattr(obj, 'x'):
obj.x = 10
is very unsatisfactory. You obviously wanted to set attribute x to a value of 10, and it didn’t work. Wouldn’t that be an error that someone needs to know about and that someone needs to handle, beyond just making sure it doesn’t kill the app immediately? So in this case the preliminary error checks and ignoring the problem is actually bad. Worst case, it takes a year until someone figures out that this code never did anything useful because the object never had the attribute x. And then says “that’s why our reports always gave the wrong results, because they checked whether attribute ‘z’ (not x) was set to 10”.
In my programming environment, reading or writing a non-existing array element is effectively a crash. That means pre-checking is an absolute necessity if the index is out of your control, and an absolute “no” if an incorrect index is a programming error. (But obj[‘x’] = 10 would create an entry, while obj.x = 10 would be a compile time error).
1