0

Can someone clarify the role of super().__init__(a, b, c) in making the second assertion pass? It will fail without it (empty string is returned from str).

Per my understanding Exception is a built-in type that takes no keyword arguments. But what exactly is happening in Exception when the super().__init__(a, b, c) is called? Can calling init like that have some unwanted side-effects?

class MyException(Exception):
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c
        super().__init__(a, b, c)

e1 = MyException('a', 'b', 'c')
assert str(e1) == "('a', 'b', 'c')"

e2 = MyException(a='a', b='b', c='c') 
assert str(e2) == "('a', 'b', 'c')" # if "super()..." part above is commented out, this assertion will not pass, because str(e2) is an empty string
13
  • 4
    If you don't call the superclass constructor, how will it initialize the internal data that it needs to generate the error string? Commented Jan 17, 2023 at 17:51
  • @Barmar: True. But how do you know for sure that internal data is initialized in Exception constructor? I mean we can assume it from behavior, but in order to make sure (and see what else is happening), I need to look at C source code for Exception, correct? Commented Jan 17, 2023 at 18:02
  • 3
    The general rule is that a subclass should always call the superclass constructor, unless for some reason it needs to override its normal initialization. Commented Jan 17, 2023 at 18:13
  • 1
    @Barmar: Why does the first assertion pass even when super is commented out? Commented Jan 17, 2023 at 19:45
  • 1
    @Barmar constructor != initializer. This code does call the superclass constructor, because the code didn't override __new__ to do otherwise. Commented Jan 18, 2023 at 5:02

1 Answer 1

1

The implementation of BaseException.__str__, which you inherit if you don't otherwise define a __str__ method, only considers the "args" tuple (src). If you pass through as keyword arguments then the "args" will be empty.

Note: it is not necessary to call super().__init__ for custom exception types, unless you need your type to work in cooperative multiple inheritance.

That's because the exception args attribute, which BaseException.__str__ uses to render the error as a string, is set by the __new__ method.

Sign up to request clarification or add additional context in comments.

4 Comments

And if I wanted my custom exception to stringify correctly when both positional (first assertion in my question) and keyword (second) args are passed in, is calling super().__init__ like in my question the way to go?
No, I don't think so. The way to go is to define your __str__ method explicitly. If you're just implementing simple custom error types, not expected to be mixed in with others, then the super() proxy will resolve on Exception -> BaseException -> object, and none of their inits do anything interesting, so no need to use super at all imo.
@wim OTOH, unless the behavior of the superclasses' initializers are documented, you should call it because you don't know that it's safe to skip it. It may be needed to ensure Liskov substitutability.
@Barmar They don't normally do this in stdlib, for example subprocess.CalledProcessError, argparse.ArgumentError. Realistically I don't think LSP is a genuine concern here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.