Why does this code raise RecursionError on line 11?

Viewed 110

Why does this code raise RecursionError on line 11?

def my_class_decorator(cls):
    class DoNothing(cls):
        pass

    return DoNothing


@my_class_decorator
class Echo:
    def __init__(self):
        super(Echo, self).__init__()
        print("hello world")


Echo()

I got the following error:

RecursionError: maximum recursion depth exceeded while calling a Python object
2 Answers

When your code executes

super(Echo, self).__init__()

Echo is the subclass created by your decorator, not the original Echo class. That means that the super(Echo, self).__init__ lookup finds the __init__ method you wrote, not object.__init__. Your __init__ method ends up calling itself over and over instead of calling object.__init__.

It's a good idea to avoid creating subclasses in decorators, to avoid this kind of problem. If you want to make a subclass anyway, you can use 0-argument super() to make sure super sees the original Echo instead of the decorator-created subclass:

@my_class_decorator
class Echo:
    def __init__(self):
        super().__init__()
        print("hello world")

The super() function is used to give access to methods and properties of a parent or sibling class. The super() function returns an object that represents the parent class. And so if we go step by step with your code.

def my_class_decorator(cls):
class DoNothing(cls):
    pass

return DoNothing


#@my_class_decorator
class Echo:
    def __init__(self):
       super(Echo, self).__init__()
       print("hello world")

Echo = my_class_decorator(Echo) # It's going to decorate your class
# Echo will be in this case the class DoNothing which is inherited from Echo 
# class 
Echo() # calling Echo means you are going to call DoNoting class and  
# which will invoke parent __init__ function. In __init__ method, we have 
# super(Echo, self).__init__() 
# which mean we will call DoNothing parent __init__ method again because the 
# class Echo decorated. Something like this will happen 
# super(DoNothing, self).__init__()
# become DoNothin cls
Related