0

Passing a class variable as an argument for a decorator function leads to a NameError for the class name. Running this:

def log(prefix=None):

    def decorator(function):
        """Decorates the function"""

        def wrapper(*args, **params):
            """Wraps the function"""
            name = "-".join([prefix, function.__name__])

            result = function(*args, **params)
            print(f"completed the execution of '{name}'")
            return result

        return wrapper

    return decorator
    
class ExampleClass:

    _CONSTANT = "test"

    def __init__(self, x):
        self._x = x

    @log(prefix=ExampleClass._CONSTANT)
    def product_of_number(self, y):
        return self._x * y

if __name__ == "__main__":
    x = ExampleClass(3)
    x.product_of_number(4)

leads to the error

Traceback (most recent call last):
  File "/home/developer/reproduce_decorator_name_space.py", line 23, in <module>
    class ExampleClass:
  File "/home/developer/reproduce_decorator_name_space.py", line 31, in ExampleClass
    @log(prefix=ExampleClass._CONSTANT)
NameError: name 'ExampleClass' is not defined

However, running this

def log(prefix=None):

    def decorator(function):
        """Decorates the function"""

        def wrapper(*args, **params):
            """Wraps the function"""
            name = "-".join([prefix, function.__name__])

            result = function(*args, **params)
            print(f"completed the execution of '{name}'")
            return result

        return wrapper

    return decorator
    
_CONSTANT = "test"
class ExampleClass:

    def __init__(self, x):
        self._x = x

    @log(prefix=_CONSTANT)
    def product_of_number(self, y):
        return self._x * y

if __name__ == "__main__":
    x = ExampleClass(3)
    x.product_of_number(4)

gives output

completed the execution of 'test-product_of_number'

Why is ExampleClass not recognized? Decorated method is within class after __init__ and references self. Error message itself refers to module ExampleClass. How does the class name not exist in name space?

Gofrette
  • 468
  • 1
  • 8
  • 18
  • 1
    Does this answer your question? [Python - Referencing class name from inside class body](https://stackoverflow.com/questions/3315510/python-referencing-class-name-from-inside-class-body). TL;DR, the name `ExampleClass` isn't assigned to until after the execution of the class block. – Brian61354270 Dec 11 '21 at 22:48
  • 1
    You should also be able to use `@log(prefix=_CONSTANT)` directly in the first snippet. – Brian61354270 Dec 11 '21 at 22:49
  • Also related https://stackoverflow.com/q/19622550/11082165 – Brian61354270 Dec 11 '21 at 22:49

1 Answers1

0

The decorator has access to ExampleClass, probably no need to pass an argument to the decorator:

def log(prefix=''):
    def decorator(function):
        """Decorates the function"""
        def wrapper(*args, **params):
            """Wraps the function"""
            # args[0] references the decorated class

            if prefix:
                # Option2: Prefix passed as string
                name = "-".join(
                    [getattr(args[0], prefix, ''), function.__name__])
            else:
                # Option1: No prefix
                name = "-".join([args[0]._CONSTANT, function.__name__])

            result = function(*args, **params)
            print(f"completed the execution of '{name}'")
            return result

        return wrapper

    return decorator


class ExampleClass:

    _CONSTANT = "test"
    _ANOTHER_CONSTANT = "test2"

    def __init__(self, x):
        self._x = x

    @log()
    def product_of_number(self, y):
        return self._x * y

    @log('_ANOTHER_CONSTANT')
    def product_of_anothernumber(self, y):
        return self._x * y


if __name__ == "__main__":
    x = ExampleClass(3)
    x.product_of_number(4)
    x.product_of_anothernumber(4)

Out:

completed the execution of 'test-product_of_number'
completed the execution of 'test2-product_of_anothernumber'
Maurice Meyer
  • 17,279
  • 4
  • 30
  • 47