Tutorial :“else” considered harmful in Python?



Question:

In an answer (by S.Lott) to a question about Python's try...else statement:

Actually, even on an if-statement, the else: can be abused in truly terrible ways creating bugs that are very hard to find. [...]

Think twice about else:. It is generally a problem. Avoid it except in an if-statement and even then consider documenting the else- condition to make it explicit.

Is this a widely held opinion? Is else considered harmful?

Of course you can write confusing code with it but that's true of any other language construct. Even Python's for...else seems to me a very handy thing to have (less so for try...else).


Solution:1

S.Lott has obviously seen some bad code out there. Haven't we all? I do not consider else harmful, though I've seen it used to write bad code. In those cases, all the surrounding code has been bad as well, so why blame poor else?


Solution:2

No it is not harmful, it is necessary.

There should always be a catch-all statement. All switches should have a default. All pattern matching in an ML language should have a default.

The argument that it is impossible to reason what is true after a series of if statements is a fact of life. The computer is the biggest finite state machine out there, and it is silly to enumerate every single possibility in every situation.

If you are really afraid that unknown errors go unnoticed in else statements, is it really that hard to raise an exception there?


Solution:3

Saying that else is considered harmful is a bit like saying that variables or classes are harmful. Heck, it's even like saying that goto is harmful. Sure, things can be misused. But at some point, you just have to trust programmers to be adults and be smart enough not to.

What it comes down to is this: if you're willing to not use something because an answer on SO or a blog post or even a famous paper by Dijkstra told you not to, you need to consider if programming is the right profession for you.


Solution:4

I wouldn't say it is harmful, but there are times when the else statement can get you into trouble. For instance, if you need to do some processing based on an input value and there are only two valid input values. Only checking for one could introduce a bug. eg:

The only valid inputs are 1 and 2:    if(input == 1)  {     //do processing     ...  }  else  {     //do processing      ...  }  

In this case, using the else would allow all values other than 1 to be processed when it should only be for values 1 and 2.


Solution:5

To me, the whole concept of certain popular language constructs being inherently bad is just plain wrong. Even goto has its place. I've seen very readable, maintainable code by the likes of Walter Bright and Linus Torvalds that uses it. It's much better to just teach programmers that readability counts and to use common sense than to arbitrarily declare certain constructs "harmful".


Solution:6

If you write:

if foo:      # ...  elif bar:      # ...  # ...  

then the reader may be left wondering: what if neither foo nor bar is true? Perhaps you know, from your understanding of the code, that it must be the case that either foo or bar. I would prefer to see:

if foo:      # ...  else:      # at this point, we know that bar is true.      # ...  # ...  

or:

if foo:      # ...  else:      assert bar      # ...  # ...  

This makes it clear to the reader how you expect control to flow, without requiring the reader to have intimate knowledge of where foo and bar come from.

(in the original case, you could still write a comment explaining what is happening, but I think I would then wonder: "Why not just use an else: clause?")

I think the point is not that you shouldn't use else:; rather, that an else: clause can allow you to write unclear code and you should try to recognise when this happens and add a little comment to help out any readers.

Which is true about most things in programming languages, really :-)


Solution:7

Au contraire... In my opinion, there MUST be an else for every if. Granted, you can do stupid things, but you can abuse any construct if you try hard enough. You know the saying "a real programer can write FORTRAN in every language".

What I do lots of time is to write the else part as a comment, describing why there's nothing to be done.


Solution:8

Else is most useful when documenting assumptions about the code. It ensures that you have thought through both sides of an if statement.

Always using an else clause with each if statement is even a recommended practice in "Code Complete".


Solution:9

The rationale behind including the else statement (of try...else) in Python in the first place was to only catch the exceptions you really want to. Normally when you have a try...except block, there's some code that might raise an exception, and then there's some more code that should only run if the previous code was successful. Without an else block, you'd have to put all that code in the try block:

try:      something_that_might_raise_error()      do_this_only_if_that_was_ok()  except ValueError:      # whatever  

The issue is, what if do_this_only_if_that_was_ok() raises a ValueError? It would get caught by the except statement, when you might not have wanted it to. That's the purpose of the else block:

try:      something_that_might_raise_error()  except ValueError:      # whatever  else:      do_this_only_if_that_was_ok()  

I guess it's a matter of opinion to some extent, but I personally think this is a great idea, even though I use it very rarely. When I do use it, it just feels very appropriate (and besides, I think it helps clarify the code flow a bit)


Solution:10

Seems to me that, for any language and any flow-control statement where there is a default scenario or side-effect, that scenario needs to have the same level of consideration. The logic in if or switch or while is only as good as the condition if(x) while(x) or for(...). Therefore the statement is not harmful but the logic in their condition is.

Therefore, as developers it is our responsibility to code with the wide scope of the else in-mind. Too many developers treat it as a 'if not the above' when in-fact it can ignore all common sense because the only logic in it is the negation of the preceding logic, which is often incomplete. (an algorithm design error itself)

I don't then consider 'else' any more harmful than off-by-ones in a for() loop or bad memory management. It's all about the algorithms. If your automata is complete in its scope and possible branches, and all are concrete and understood then there is no danger. The danger is misuse of the logic behind the expressions by people not realizing the impact of wide-scope logic. Computers are stupid, they do what they are told by their operator(in theory)

I do consider the try and catch to be dangerous because it can negate handling to an unknown quantity of code. Branching above the raise may contain a bug, highlighted by the raise itself. This is can be non-obvious. It is like turning a sequential set of instructions into a tree or graph of error handling, where each component is dependent on the branches in the parent. Odd. Mind you, I love C.


Solution:11

There is a so called "dangling else" problem which is encountered in C family languages as follows:

if (a==4)  if (b==2)  printf("here!");  else  printf("which one");  

This innocent code can be understood in two ways:

if (a==4)      if (b==2)          printf("here!");      else          printf("which one");  

or

if (a==4)      if (b==2)          printf("here!");  else      printf("which one");  

The problem is that the "else" is "dangling", one can confuse the owner of the else. Of course the compiler will not make this confusion, but it is valid for mortals.

Thanks to Python, we can not have a dangling else problem in Python since we have to write either

if a==4:      if b==2:          print "here!"  else:      print "which one"  

or

if a==4:      if b==2:          print "here!"      else:          print "which one"  

So that human eye catches it. And, nope, I do not think "else" is harmful, it is as harmful as "if".


Solution:12

In the example posited of being hard to reason, it can be written explicitly, but the else is still necessary. E.g.

if a < 10:             # condition stated explicitly     elif a > 10 and b < 10:             # condition confusing but at least explicit     else:             # Exactly what is true here?             # Can be hard to reason out what condition is true  

Can be written

if a < 10:             # condition stated explicitly     elif a > 10 and b < 10:             # condition confusing but at least explicit     elif a > 10 and b >=10:      # else condition  else:         # Handle edge case with error?  


Solution:13

I think the point with respect to try...except...else is that it is an easy mistake to use it to create inconsistent state rather than fix it. It is not that it should be avoided at all costs, but it can be counter-productive.

Consider:

try:      file = open('somefile','r')  except IOError:      logger.error("File not found!")  else:      # Some file operations      file.close()  # Some code that no longer explicitly references 'file'  

It would be real nice to say that the above block prevented code from trying to access a file that didn't exist, or a directory for which the user has no permissions, and to say that everything is encapsulated because it is within a try...except...else block. But in reality, a lot of code in the above form really should look like this:

try:      file = open('somefile','r')  except IOError:      logger.error("File not found!")      return False  # Some file operations  file.close()  # Some code that no longer explicitly references 'file'  

You are often fooling yourself by saying that because file is no longer referenced in scope, it's okay to go on coding after the block, but in many cases something will come up where it just isn't okay. Or maybe a variable will later be created within the else block that isn't created in the except block.

This is how I would differentiate the if...else from try...except...else. In both cases, one must make the blocks parallel in most cases (variables and state set in one ought to be set in the other) but in the latter, coders often don't, likely because it's impossible or irrelevant. In such cases, it often will make a whole lot more sense to return to the caller than to try and keep working around what you think you will have in the best case scenario.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »