版权申明:本文为博主窗户(Colin Cai)原创,欢迎转帖。如要转贴,必须注明原文网址 http://www.cnblogs.com/Colin-Cai/p/12977127.html 作者:窗户 QQ/微信:6679072 E-mail:6679072@qq.com
理论上,函数是一等公民(first class function)的语言都可以使用函数式编程,从而利用算子(高阶函数)来做装饰器。
装饰器一般是这样一个算子,它接受一个函数作为参数,返回另外一个函数。装饰器,顾名思义,就是把一个函数“装饰”一下,得到另外一个函数。为何要装饰一下呢?目的一般是可能设计上需要对函数做一些改装,比如原函数输出结果需要再加工加工,或者原函数的输入参数传入不一样,或者两者兼有之,等等。
迭代是编程中常用的手段,它的计算方式表现为状态的不断变换,且状态的变换具有唯一性。
比如我们使用Scheme来表示迭代。
;stat代表当前状态,next代表状态改变函数,final?代表判断状态是否终止 (define (iterate-orgin stat next final?) (if (final? stat) stat (iterate-orgin (next stat) next final?))) ;将next函数和final?函数封成一个函数 (define (iterate stat f-stat) (iterate-orgin stat (f-stat 'next) (f-stat 'final))) ;最终我们需要的迭代函数 (define (it f-stat) (lambda (stat) (iterate stat f-stat)))
以上构造出一个算子it,就是用来“装饰”迭代的函数。
我们构造一个对list求和的迭代:
可以每次把list的前面两个相加,比如对(1 2 3 4 5)求和,经过以下状态:
(1 2 3 4 5)
(3 3 4 5)
(6 4 5)
(10 5)
(15)
15
得到最后结果15。
代码可以如下:
(define (make-sum-func sym) (if (eq? sym 'next);next函数 (lambda (lst) (if (pair? lst) (if (null? (cdr lst)) (car lst) (cons (+ (car lst) (cadr lst)) (cddr lst))) lst)) (if (eq? sym 'final?);final?函数 (lambda (lst) (not (pair? lst))) '())))
然后测试一下((it make-sum-func) '(1 2 3 4 5)),得到最后结果15。
上面两个函数写在一起,我们还可以再分离一下。
;定义一个打包函数 (define (wrap-next-final next final?) (lambda (sym) (if (eq? sym 'next) next (if (eq? sym 'final?) final? '())))) ;下面next和final?两个函数可以分开写 (define make-sum-next (lambda (lst) (if (pair? lst) (if (null? (cdr lst)) (car lst) (cons (+ (car lst) (cadr lst)) (cddr lst))) lst))) (define make-sum-final? (lambda (lst) (not (pair? lst)))) ;于是函数就可以下面这样表示 (define make-sum-func (wrap-next-final make-sum-next make-sum-final?))
总而言之,装饰器就是这样一类算子。
Python也是这样,只是Python提供了@这样的语法,实际上是个语法糖,与其说是简写,倒是更像是个语法提醒这是一个装饰器。
我们这次希望来显示一下mysym,还是用求和。
先写一个简单的mysum函数用于求和:
def mysum(*args): ret = 0 for i in args: ret += i return ret
做一个算子,来扩充它的输入参数:
这里需要用来判断一个对象是否是可迭代对象,
from collections import Iterable
然后,如果判断对象x是否是可迭代对象,只需要:
isinstance(x, Iterable)
算子如下:
from collections import Iterable def args_expan(f): def f2(*args): lst = [] for i in args: if isinstance(i, Iterable): lst.append(f(*i)) else: lst.append(i) return f(*lst) return f2
然后在mysum前添加装饰器标志
@args_expan def mysum(*args): ret = 0 for i in args: ret += i return ret
测试如下:
print(mysum(1,2,3,4,5)) print(mysum((1,2,3,4,5))) print(mysum([1,2,3,4,5])) print(mysum(range(1,6))) print(mysum(map(lambda x:x+1, range(5)))) print(mysum(filter(lambda x:x<6, range(10)))) #构造了一个生成器 def gen_range(a, b): while a < b: yield a a += 1 print(mysum(\ filter(lambda x:x<6, range(10)), \ 6, \ [7,8], \ (9, 10), \ map(lambda x:x+11, range(10)), \ gen_range(21,101)))
运行得到:
15
15
15
15
15
15
5050
终于看到,函数功能已被扩充。
这个装饰器还可以装饰别的函数,比如乘积、统计等。
从而,装饰器就是这样一个算子,一般用来改造函数的输入或输出,避免重复写代码。