1

Lets say my class has many function, and I want to apply my decorator on each one of them. I have researched for a while, and find https://stackoverflow.com/a/6307917/18859252. By using metaclass, I can decorate all function in one line. Here is my code (framework)

class myMetaClass(type):
   def __new__(cls, name, bases, local):
      for attr in local:
         value = local[attr]
         if callable(value) and attr != '__init__':
            local[attr] = log_decorator(local['Variable'], value)
      return super().__new__(cls, name, bases, local)


class log_decorator():
   def __init__(self, Variable, func):
       self.Variable = Variable
       self.func = func

   def __call__(self, *args, **kargs):
       start_time = time.time()
       self.func(*args, **kargs)
       end_time = time.time()


class Test(metaclass = myMetaClass):

   Variable = Some_Class
   check_test = Some_Class

   def __init__(self, **args):
       self.connect = Some_Class(**args)

   def A(self, a, b):
       self.connect.abc
       pass

then use like this

def Flow():
   test = Test(**args)
   test.A(a, b)

But here is problem, it show exception like:

TypeError:A() missing 1 required positional argument: 'self'

I have no idea about this problem. I'd be very grateful if anyone has an answer or if there is a better way.

Qian
  • 23
  • 5

1 Answers1

0

The piece you are missing (and the bit I don't fully understand, but has to do with functions or methods as descriptors and how python will attach an instance as the self parameter) is that log_decorator() is an instance of that class and not a function or method (even though you have defined a __call__() method which makes it callable.)

Here is some code which just slightly changes the syntax needed, but gives you the results you want:

import functools

class log_decorator:
    def __init__(self, Variable):  # Note that the only parameter is Variable
       self.Variable = Variable

    def __call__(self, func):
        @functools.wraps(func)
        def decorated(*args, **kwargs):
           start_time = time.time()
           func(*args, **kwargs)
           end_time = time.time()

        return decorated

class myMetaClass(type):
   def __new__(cls, name, bases, local):
      for attr in local:
         value = local[attr]
         if callable(value) and attr != '__init__':
            # Note the change in syntax vvv
            local[attr] = log_decorator(local['Variable'])(value)
      return super().__new__(cls, name, bases, local)
quamrana
  • 37,849
  • 12
  • 53
  • 71
  • It work! thanks for your answer. But this is so hard to understand.... – Qian Oct 17 '22 at 01:18
  • Take a look at this extensive [answer](https://stackoverflow.com/a/1594484) as it contains everything you need to know about decorators. My line: `local[attr] = log_decorator(local['Variable'])(value)` is like adding `@log_decorator(Variable)` before every method. – quamrana Oct 17 '22 at 09:46