Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do GeneratorExit and StopIteration have different base classes?

I was taking a look at the hierarchy of the built-in python exceptions, and I noticed that StopIteration and GeneratorExit have different base classes:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StandardError
      +-- Warning

Or in code:

>>> GeneratorExit.__bases__
(<type 'exceptions.BaseException'>,)
>>> StopIteration.__bases__
(<type 'exceptions.Exception'>,)

When I go to the specific description of each exception, I can read following:

https://docs.python.org/2/library/exceptions.html#exceptions.GeneratorExit

exception GeneratorExit

Raised when a generator‘s close() method is called. It directly inherits from BaseException instead of StandardError since it is technically not an error.

https://docs.python.org/2/library/exceptions.html#exceptions.StopIteration

exception StopIteration

Raised by an iterator‘s next() method to signal that there are no further values. This is derived from Exception rather than StandardError, since this is not considered an error in its normal application.

Which is not very clear to me. Both are similar in the sense that they do not notify errors, but an "event" to change the flow of the code. So, they are not technically errors, and I understand that they should be separated from the rest of the exceptions... but why is one a subclass of BaseException and the other one a subclass of Exception?.

In general I considered always that Exception subclasses are errors, and when I write a blind try: except: (for instance calling third party code), I always tried to catch Exception, but maybe that is wrong and I should be catching StandardError.

like image 739
bgusach Avatar asked Jul 14 '15 12:07

bgusach


People also ask

What is the parent class of ZeroDivisionError?

There are different kind of exceptions like ZeroDivisionError, AssertionError etc. All exception classes are derived from the BaseException class.

Which classes are exception classes in Python?

The BaseException class is, as the name suggests, the base class for all built-in exceptions in Python. Typically, this exception is never raised on its own, and should instead be inherited by other, lesser exception classes that can be raised.

Which class is considered as root class for exception handling in Python?

In Python, all exceptions must be instances of a class that derives from BaseException . In a try statement with an except clause that mentions a particular class, that clause also handles any exception classes derived from that class (but not exception classes from which it is derived).

Which of the following built-in exceptions is the root class for all exceptions?

exception Exception The root class for exceptions. All built-in exceptions are derived from this class. All user-defined exceptions should also be derived from this class, but this is not (yet) enforced.


1 Answers

It is quite common to use try: ... except Exception: ... blocks.

If GeneratorExit would inherit from Exception you would get the following issue:

def get_next_element(alist):
    for element in alist:
        try:
            yield element
        except BaseException:  # except Exception
            pass

for element in get_next_element([0,1,2,3,4,5,6,7,8,9]):
    if element == 3:
        break
    else:
        print(element)

0
1
2
Exception ignored in: <generator object get_next_element at 0x7fffed7e8360>
RuntimeError: generator ignored GeneratorExit

This example is quite simple but imagine in the try block a more complex operation which, in case of failure, would simply ignore the issue (or print a message) and get to the next iteration.

If you would catch the generic Exception, you would end up preventing the user of your generator from breaking the loop without getting a RuntimeError.

A better explanation is here.

EDIT: answering here as it was too long for a comment.

I'd rather say the opposite. GeneratorExit should inherit from Exception rather than BaseException. When you catch Exception you basically want to catch almost everything. BaseException as PEP-352 states, is for those exceptions which need to be "excepted" in order to allow the user to escape from code that would otherwise catch them. In this way you can, for example, still CTRL-C running code. GeneratorExit falls into that category in order to break loops. An interesting conversation about it on comp.lang.python.

like image 163
noxdafox Avatar answered Nov 15 '22 20:11

noxdafox