Python AbstractMethods 上的装饰器

Kri*_*ish 9 python python-3.x

我在 Python 中有一个抽象基类,它定义了一个抽象方法。我想用一个定时器函数来装饰它,这样每个扩展和实现这个基类的类都是定时的,不需要手动注释。这是我所拥有的

import functools
import time
import abc


class Test(metaclass=abc.ABCMeta):
    @classmethod
    def __subclasshook__(cls, subclass):
        return (hasattr(subclass, 'apply') and
                callable(subclass.apply))

    @abc.abstractmethod
    def apply(self, a: str) -> str:
        raise NotImplementedError

    def timer(func):
        @functools.wraps(func)
        def wrapper_timer(*args, **kwargs):
            start_time = time.perf_counter()
            value = func(*args, **kwargs)
            end_time = time.perf_counter()
            run_time = end_time - start_time
            print(f"Finished {func.__name__!r} in {run_time:.4f} secs")
            return value

        return wrapper_timer

    def __getattribute__(self, name):
        if name == "apply":
            func = getattr(type(self), "apply")
            return self.timer(func)
        return object.__getattribute__(self, name)


class T2(Test):
    def apply(self, a: str) -> str:
        return a

if __name__ == '__main__':
    t = T2()
    t.apply('a')
Run Code Online (Sandbox Code Playgroud)

我得到的错误如下

Traceback (most recent call last):
  File "/Users/blah/test.py", line 41, in <module>
    t.apply('a')
  File "/Users/blah/test.py", line 20, in wrapper_timer
    value = func(*args, **kwargs)
TypeError: apply() missing 1 required positional argument: 'a'
Run Code Online (Sandbox Code Playgroud)

我认为理解错误 python 认为 T2() 对象的 apply 方法是 aclassmethod但是我不确定为什么我调用getattr(type(self), "apply"). 有没有办法获取实例方法?

che*_*ner 7

使用__init_subclass__应用定时器装饰为您服务。(timer顺便说一下,不需要在类中定义;它比那更通用。)__init_subclass__也是确定是否apply可调用的更合适的地方。

import abc
import functools
import time


def timer(func):
    @functools.wraps(func)
    def wrapper_timer(*args, **kwargs):
        start_time = time.perf_counter()
        value = func(*args, **kwargs)
        end_time = time.perf_counter()
        run_time = end_time - start_time
        print(f"Finished {func.__name__!r} in {run_time:.4f} secs")
        return value
    return wrapper_timer


class Test(metaclass=abc.ABCMeta):
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        # ABCMeta doesn't let us get this far if cls.apply isn't defined
        if not callable(cls.apply):
            raise TypeError("apply not callable")
        cls.apply = timer(cls.apply)

    @abc.abstractmethod
    def apply(self, a: str) -> str:
        raise NotImplementedError

class T2(Test):
    def apply(self, a: str) -> str:
        return a

if __name__ == '__main__':
    t = T2()
    t.apply('a')
Run Code Online (Sandbox Code Playgroud)