面向切面编程在 Python 中一部分体现为装饰器。

由于 Python 中一切皆对象,装饰器的使用方法也因此多种多样,下文介绍装饰器的四种写法。

用函数装饰函数

def retry(func: Optional[Callable] = None, duration: timedelta = timedelta(seconds=2), limit: int = 10) -> Callable:
    if not func:
        return functools.partial(retry, duration=duration, limit=limit)

    @functools.wraps(func)
    def _func(*args, **kwargs):
        duration_seconds = duration.total_seconds()
        count = 1
        while count <= limit:
            try:
                result = func(*args, **kwargs)
                return result
            except:
                count += 1
                time.sleep(duration_seconds)
                continue

    return _func

class TestRetry(unittest.TestCase):

    @staticmethod
    def generate_test_function(limit, retry_decorator):
        retry_count = [0]

        @retry_decorator(limit=5, duration=timedelta(seconds=0))
        def my_test():
            retry_count[0] += 1
            if retry_count[0] < limit:
                raise Exception("Test failed")
            else:
                return retry_count

        return my_test

    def test_function_decoration(self):

        for i in range(100):
            exception_count = random.randint(1, 10)
            retry_count = self.generate_test_function(exception_count, retry)()
            if exception_count <= 5:
                self.assertEqual(retry_count[0], exception_count)
            else:
                self.assertIsNone(retry_count)

这是最常见的一种写法,单元测试中的语法糖拆解过程如下:

$$mytest() = retry\_decorator(limit=5, duration=timedelta(seconds=0))(my\_test)()$$

用类装饰函数

class Retry:
	"""
	Decorator
	"""

    def __init__(self, duration: timedelta = timedelta(seconds=2), limit: int = 10):
        self.duration = duration
        self.limit = limit

    def __call__(self, func: Callable):

        @functools.wraps(func)
        def _func(*args, **kwargs):
            duration_seconds = self.duration.total_seconds()
            count = 1
            while count <= self.limit:
                try:
                    result = func(*args, **kwargs)
                    return result
                except:
                    count += 1
                    time.sleep(duration_seconds)
                    continue
        return _func



class TestRetry(unittest.TestCase):
	"""
	unittest
	"""

    @staticmethod
    def generate_test_function(limit, retry_decorator):
        retry_count = [0]

        @retry_decorator(limit=5, duration=timedelta(seconds=0))
        def my_test():
            retry_count[0] += 1
            if retry_count[0] < limit:
                raise Exception("Test failed")
            else:
                return retry_count

        return my_test

    def test_class_decoration(self):

        for i in range(100):
            exception_count = random.randint(1, 10)
            retry_count = self.generate_test_function(exception_count, Retry)()
            if exception_count <= 5:
                self.assertEqual(retry_count[0], exception_count)
            else:
                self.assertIsNone(retry_count)

用类装饰函数并不常见,这里主要运用 class 的 __call__ 特性,语法糖分解后

$$my\texttt{_}test() = TestRetry(limit=5, duration=timedelta(seconds=0)).\texttt{__call__}(self, my\_test)$$

用函数装饰类

def single_instance(clazz: type) -> type:
    """
    Decorator to make a class a singleton.
    """
    class Singleton:

        delegate_clazz = clazz

        instance = None
        delegate_instance = None

        def __init__(self, *args, **kwargs):
            self.delegate_instance.__init__(*args, **kwargs)

        def __new__(cls, *args, **kwargs):
            if cls.instance is None:
                cls.instance = super().__new__(cls)
                cls.delegate_instance = cls.delegate_clazz.__new__(cls.delegate_clazz, *args, **kwargs)
            return cls.instance

        def __getattr__(self, item):
            return getattr(self.delegate_instance, item)

    return Singleton


class TestClass(unittest.TestCase):

    def test_single_instance_func(self):
        @single_instance
        class Instance:
            def __init__(self, num):
                self.num = num

            def value(self):
                return self.num

        self.assertEqual(Instance(1), Instance(1))
        self.assertEqual(Instance(1).num, 1)
        self.assertEqual(Instance(1).value(), 1)

这里用函数装饰器实现了单例模式,但生产场景中很少如此实现,大多采用元类的方式。语法糖拆解如下

$$ Instance(1) = single\_instance(Instance).\texttt{__new__}(Singleton).\texttt{__init__}(self, 1) $$

用类装饰函数

class SingleInstance:
	"""
    Decorator to make a class a singleton.
    """

    def __init__(self, clazz: type):
        self.clazz = clazz
        self.instance = None

    def __call__(self, *args, **kwargs):
        if self.instance is None:
            self.instance = self.clazz(*args, **kwargs)
        else:
            self.instance.__init__(*args, **kwargs)
        return self.instance


class TestClass(unittest.TestCase):

    def test_single_instance_func(self):
        @SingleInstance
        class Instance:
            def __init__(self, num):
                self.num = num

            def value(self):
                return self.num

        self.assertEqual(Instance(1), Instance(1))
        self.assertEqual(Instance(1).num, 1)
        self.assertEqual(Instance(1).value(), 1)

类似用类装饰器装饰函数,语法糖拆解如下

$$Instance(1) = SingleInstance(Instance)(1) = SingleInstance(Instance).\texttt{__call__}(self, 1) $$