Python修饰器模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/duxu24/article/details/72859918

无论何时,在对一个对象添加额外功能时,都有以下可选方法:

  • 如果合理,直接将功能添加到对象所属的类;
  • 使用组合;
  • 使用继承。

设计模式为我们提供了第四种可选方法,以支持动态地(运行时)扩展一个对象的功能,这种方法就是修饰器(Decorator)

  • 修饰器模式能够以透明的方式(不影响其他对象)动态地将功能添加到一个对象中。
  • 在很多编程语言里,使用继承来实现修饰器模式。在Python中,可以使用内置的修饰器特性,一个Python修饰器就是对Python语法的特定改变,用于扩展一个类、方法或函数的行为,而无需使用继承。
  • 从实现的角度来说,Python修饰器是一个可调用对象(函数、方法、类),接受一个函数对象fin作为输入,并返回另一个函数对象fout。

应用案例

应用Python修饰器模式,解决实现斐波那契数列的性能问题。

首先来看看朴素的实现方法:

def fibonacci(n):
    assert(n >= 0), 'n must be >= 0!'
    return n if n in (0, 1) else fibonacci(n-1) + fibonacci(n-2)

if __name__ == '__main__':
    from timeit import Timer

    t = Timer('fibonacci(10)', 'from __main__ import fibonacci')
    print(t.timeit())


----------
输出:
25.401731605998066

执行结果可知,朴素方法速度有多慢!计算第10个斐波那契数就要25秒。

使用memoization提速递归函数

known = {0:0, 1:1}
def fibonacci(n):
    assert(n >= 0), 'n must be >= 0!'
    if n in known:
        return known[n]
    res = fibonacci(n-1) + fibonacci(n-2)
    known[n] = res
    return res

if __name__ == '__main__':
    from timeit import Timer

    t = Timer('fibonacci(10)', 'from __main__ import fibonacci')


----------
输出:
0.1905040599995118

在上述代码中,我们使用了一个dict来缓存斐波那契数列中已经计算好的数值。同样计算第10个斐波那契数,可以看到性能得到了极大地提升。
但该方法有一些问题。虽然性能得到提升,但代码也没有不使用memoization时那样简洁。如何能在保持递归函数与朴素版本一样简单,在性能上又能与使用memoization的函数相近呢?解决方法就是使用修饰器模式。

修饰器模式

def memoize(fn):
    know = dict()

    def memoizer(*args):
        if args not in know:
            know[args] = fn(*args)
        return know[args]

    return memoizer

@memoize
def fibonacci(n):
    assert(n >= 0),'n must be >= 0!'
    return n if n in (0,1) else fibonacci(n-1)+fibonacci(n-2)

if __name__ == '__main__':
    from timeit import Timer

    t = Timer('fibonacci(100)', 'from __main__ import fibonacci')
    print(t.timeit())


----------
输出:
0.3000087060026999

首先创建了一个memoize()函数,这个修饰器接受一个需要使用memoization的函数fn作为输入,使用一个字典作为缓存。修饰使用@name语法,其中name是指我们想要使用的修饰器的名称。这其实只不过是一个简化修饰器使用的语法糖。
从输出可以看出:计算第100个斐波那契数只需要约0.3秒。这一方案同时具备可读的代码和可接受的性能。

另外,再看一个例子:给定一个混合大小写,并带有标点符号的字符串,使用装饰器进行操作:

  • 清除标点符号
  • 转化为小写
  • 最后形成词的列表
from string import punctuation

def pipeline_wrapper(func):
    def to_lower(x):
        return x.lower()

    def remove_punc(x):
        for i in punctuation:
            x = x.replace(i, '')
        return x

    def wrapper(*args, **kwargs):
        x = to_lower(*args, **kwargs)
        x = remove_punc(x)

        return func(x)

    return wrapper

@pipeline_wrapper
def func(intext):
    return intext.split()

if __name__ == '__main__':
    intest = 'stringt. With. Punctuation?'
    print(func(intest))


----------
输出:
['stringt', 'with', 'punctuation']

所以,修饰器模式可以扩展一个对象的行为,无需使用继承,非常方便。Python进一步扩展了修饰器的概念,允许我们无需使用继承或组合就能扩展任意可调用对象(函数、方法、类)的行为。

猜你喜欢

转载自blog.csdn.net/duxu24/article/details/72859918