Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

__repr__ method appears can't be invoked automatically for Exception class

I was told that if an obj's __str__ method isn't created but __repr__ is created then printing the obj will invoke __repr__. That appears to be true for normal class, but when I try it on Exception's subclass, it is weird that __repr__ doesn't get invoked, could anyone explain why?

Here's an example

class BoringError(Exception):
  def __init__(self):
    self.purpose = "Demonstration"
    self.boringLevel = 10
  def __repr__(self):
    return "I'm a message for developer"

 try: 
   if 1 != 2:
    raise BoringError 
 except BoringError as e:
   print(e.boringLevel)
   print(e)

class Tree:
  def __repr__(self):
    return "I love water"

AVL = Tree()
print(AVL)

This program produces the following result

10

I love water

Instead of

10
I'm a message for developer
I love water
like image 421
Stephen Fong Avatar asked May 07 '19 13:05

Stephen Fong


2 Answers

__repr__ is only invoked if e.__str__ resolves to object.__str__, which is basically defined like

def __str__(self):
    return self.__repr__()

In your case, object.__str__ is never invoked, because e.__str__ resolves to Exception.__str__, which does not invoke any definition of __repr__. Compare:

>>> e = Exception("hi")
>>> print(e)
hi
>>> str(e)
'hi'
>>> repr(e)
"Exception('hi')"

Tree.__str__ is not defined, so AVL.__str__ does resolve to object.__str__, hence Tree.__repr__ gets called.

like image 52
chepner Avatar answered Oct 03 '22 23:10

chepner


When an exception is printed __str__ is called not __repr__. See the handling exception documentation:

The except clause may specify a variable after the exception name. The variable is bound to an exception instance with the arguments stored in instance.args. For convenience, the exception instance defines __str__() so the arguments can be printed directly without having to reference .args. One may also instantiate an exception first before raising it and add any attributes to it as desired.

Consider the following:

try:
    raise NameError('HiThere')
except NameError as e:
    print(e)

When print(e) is called, the instance e has a __str__ method that returns the arguments it was created with, in this case the string 'HiThere'.

That's how you can use print(e) instead of explicitely using print(e.args).

In your case, there are no arguments passed to BoringError, therefore print(e) prints an empty string.

Overriding the __str__ method with:

 def __str__(self):
     return "I'm a message for developer"

gives the expected result.

like image 42
Jacques Gaudin Avatar answered Oct 04 '22 00:10

Jacques Gaudin