4

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
  • 2
    That is some mighty ugly C++ code. Maybe you've been writing Java for too long. Assuming node is a map, the usual C++ code would be simply: node['x'] = 20, and no exception would be possible. Jan 10, 2013 at 19:33
  • Thanks @kevincline, I've updated the code. The issue is not Java, but actually working with a Xerces DOM object :) Jan 10, 2013 at 19:44
  • Option 1 looks like it packs the same amount of logic into half of the lines of code, and remains equally (if not more) readable. I say go with that.
    – MrFox
    Jan 10, 2013 at 20:27

3 Answers 3

3

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).

6

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
  • "No premature optimisation" is one thing, "no easily avoided slowdown" is another.
    – gnasher729
    Oct 10, 2021 at 9:36
0

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
  • Oh my... I didn't even remember asking this question so long ago :) Honestly - the example in the question is awful for the matter. And it made the whole conversation go wrong. Because really it wasn't about attributes, or arrays access. It was literally about me misunderstanding EAFP back then. P.S. thank you for taking your time to answer this! Oct 12, 2021 at 16:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.