某些时候我们想问多个函数统一添加某种功能,比如计时统计、记录日志、缓存运算结果等等。我们不想在每个函数内添加完全相同的代码,如何更好的达成目的呢?
要求:不在每个函数内添加完全相同的代码的前提下实现功能。
解决方案:
定义装饰器函数,用它生成一个在原函数基础上添加了新功能的函数,替代原函数。
- 对于装饰器decorator:
Python的装饰器decorator本质上是一个高阶函数,它接收一个函数作为参数,然后返回一个新的函数,可以让该函数在不改动源代码的情况下增加其他新功能。
Python通过一个语法糖@
符号来使用decorator,这样可以避免编写f = decorate(f)
这样形式的代码。所谓的语法糖便是你不使用也可以完成任务,但是使用它可以让你的代码更简洁。
对于装饰器,需要记住的就是
@decorate
def f():
pass
其中,
@decorate 等价于 f = decorate(f)
- 方案示例:
1. 斐波那契数列
def fib(n):
if n <= 1:
return 1
return fib(n-1) + fib(n-2)
print(fib(50))
2. 走台阶问题
# 台阶有100阶,一个人每次可以走1~3阶。有多少种走法?
def climb(n, steps):
count = 0
if n == 0:
count = 1
elif n > 0:
for step in steps:
count += climb(n-step, steps)
return count
print(climb(100, (1, 2, 3)))
对于求斐波那契数列,递归过程中会反复求值,造成算法效率低;走台阶问题也是如此。
如何提高上面两函数的算法效率,可以通过装饰器函数来缓存运算结果。
def memo(func):
cache = {}
def wrap(*args):
res = cache.get(args)
if not res:
res = cache[args] = func(*args)
return res
return wrap
@memo #等价于 fib = memo(fib)
def fib(n):
if n <= 1:
return 1
return fib(n-1) + fib(n-2)
print(fib(50))
@memo #等价于 climb = memo(climb)
def climb(n, steps):
count = 0
if n == 0:
count = 1
elif n > 0:
for step in steps:
count += climb(n-step, steps)
return count
print(climb(100, (1, 2, 3)))
结果:
20365011074
180396380815100901214157639
有了装饰器函数装饰之后,会很快得到运算结果,大大提高了算法效率。