Search code examples
pythondecoratorpython-decorators

Why both of the custom decorators are called even if I only called one of them?


This is my custom decorator class. When I decorate them for functions and classes, auxiliary info is printed if these items are used.

# decorator.py
class BaseDecorator(object):
    """
    Base decorator class
    """

    def __init__(self, message, source=''):
        self.message = message
        self.source = source

    def __call__(self, func):
        print(f"{type(func).__name__.capitalize()} `{func.__name__}` {self.message}")
        if self.source:
            print(f"Source: {self.source}")

        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)

        return wrapper

class Important(BaseDecorator):
    """
    The decorated content is of great importance!
    """

    def __init__(self, reason: str = '** No reason given. **'):
        super().__init__("is important: " + reason)


class Copied(BaseDecorator):
    """
    The decorated content is copied from somewhere.
    """

    def __init__(self, source=''):
        super().__init__("is copied.", source)

And when I use them like this:

from decorators_module import Important, Copied  # Assuming these decorators are defined in another module

class Solution:
    @Important(reason="This function does something crucial.")
    def critical_task(self):
        ...

    @Copied(source="Inspired by StackOverflow")
    def helper_function(self):
        ...

if __name__ == '__main__':
    solution = Solution()
    solution.critical_task()  # Only this function is called

Both the information in Copied and Important is printed. As I expected, only information in Important should be printed.

These are the actual outputs:

Function `critical_task` is important: This function does something crucial.
Function `helper_function` is copied.
Source: Inspired by StackOverflow

Can anyone help me to figure out the problem? Great appreciation in advance!


Solution

  • Your print staments are in the wrong place. Move then thus:

    # decorator.py
    class BaseDecorator(object):
        """
        Base decorator class
        """
    
        def __init__(self, message, source=''):
            self.message = message
            self.source = source
    
        def __call__(self, func):
    
            def wrapper(*args, **kwargs):
                print(f"{type(func).__name__.capitalize()} `{func.__name__}` {self.message}")
                if self.source:
                    print(f"Source: {self.source}")
                return func(*args, **kwargs)
    
            return wrapper