版权申明:本文为博主窗户(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)))

  运行得到:

  终于看到,函数功能已被扩充。

  这个装饰器还可以装饰别的函数,比如乘积、统计等。

  从而,装饰器就是这样一个算子,一般用来改造函数的输入或输出,避免重复写代码。

Python装饰器的一点解读的更多相关文章

  1. Python装饰器完全解读

    1 引言 装饰器(Decorators)可能是Python中最难掌握的概念之一了,也是最具Pythonic特色的技巧,深入理解并应用装饰器,你会更加感慨——人生苦短,我用Python. 2 初步理解装 ...

  2. Python装饰器详解

    python中的装饰器是一个用得非常多的东西,我们可以把一些特定的方法.通用的方法写成一个个装饰器,这就为调用这些方法提供一个非常大的便利,如此提高我们代码的可读性以及简洁性,以及可扩展性. 在学习p ...

  3. python装饰器方法

    前几天向几位新同事介绍项目,被问起了@login_required的实现,我说这是django框架提供的装饰器方法,验证用户是否登录,只要这样用就行了,因为自己不熟,并没有做过多解释. 今天查看dja ...

  4. python 装饰器 一篇就能讲清楚

    装饰器一直是我们学习python难以理解并且纠结的问题,想要弄明白装饰器,必须理解一下函数式编程概念,并且对python中函数调用语法中的特性有所了解,使用装饰器非常简单,但是写装饰器却很复杂.为了讲 ...

  5. Python装饰器模式学习总结

    装饰器模式,重点在于装饰.装饰的核心仍旧是被装饰对象. 类比于Java编程的时候的包装模式,是同样的道理.虽然概念上稍有不同但是原理上还是比较相近的.下面我就来谈一谈我对Python的装饰器的学习的一 ...

  6. 如何理解Python装饰器

    如何理解Python装饰器?很多学员对此都有疑问,那么上海尚学堂python培训这篇文章就给予答复. 一.预备知识 首先要理解装饰器,首先要先理解在 Python 中很重要的一个概念就是:“函数是 F ...

  7. 利用世界杯,读懂 Python 装饰器

    Python 装饰器是在面试过程高频被问到的问题,装饰器也是一个非常好用的特性, 熟练掌握装饰器会让你的编程思路更加宽广,程序也更加 pythonic. 今天就结合最近的世界杯带大家理解下装饰器. 德 ...

  8. [转]python 装饰器

    以前你有没有这样一段经历:很久之前你写过一个函数,现在你突然有了个想法就是你想看看,以前那个函数在你数据集上的运行时间是多少,这时候你可以修改之前代码为它加上计时的功能,但是这样的话是不是还要大体读读 ...

  9. Python高级特性: 12步轻松搞定Python装饰器

    12步轻松搞定Python装饰器 通过 Python 装饰器实现DRY(不重复代码)原则:  http://python.jobbole.com/84151/   基本上一开始很难搞定python的装 ...

随机推荐

  1. centos安装libconfig

    安装很简单,生成的.so文件会被安装到/usr/local/lib目录,记得修改/etc/profile. 安装过程会出现两个错误: What is makeinfo, and how do I ge ...

  2. P2308 添加括号(dfs记录dp路径)

    传送门 \(一看肯定是区间DP(因为和和合并石子很相似,都要加n-1次)\) \(转移方程为(其中he[i][j]是i到j的和)\) \[dp[i][j]=min(dp[i][j],dp[i][k]+ ...

  3. Polycarp and Div 3 CodeForces - 1005D

    这个题目其实很简单,有很多的方法写,然后我还是不会写,感觉自己好菜, 我开始想的是dp,但是不知道怎么dp,看了网上题解,豁然开朗 dp[i] 表示前面i个数满足条件的数有多少,f[s]表示前缀和为s ...

  4. JS实现显示来访者的停留时间

    显示来访者的停留时间 <script language="javascript"> var ap_name = navigator.appName; var ap_vi ...

  5. MongoDB最佳安全实践

    在前文[15分钟从零开始搭建支持10w+用户的生产环境(二)]中提了一句MongoDB的安全,有小伙伴留心了,在公众号后台问.所以今天专门开个文,写一下关于MongoDB的安全. 一.我的一次Mong ...

  6. linux centos7搭建redis-5.0.5

    1. 下载redis 1.1 下载地址 http://download.redis.io/releases/ 1.2 安装版本 redis-5.0.5.tar.gz 2. 安装redis 2.1 前置 ...

  7. 设计模式之GOF23外观模式

    外观模式 迪米特原则:一个软件实体应当尽可能少的与其他实体发生相互作用 外观模式核心:为子系统提供统一的入口,封装子系统的复杂性,便于客户端调用 相当于找了个代理帮你做了所有事而你只需要和代理打交道

  8. 2018-06-18 Javascript 基础1

    js是一门基于对象的若类型语言,他和JAVA没有关系: js标签放置位置:1.内联 2.外部 3.内部: js代码是有执行顺序的,这点和css代码有所区别:当对象没有加载完或者还没加载的情况下js代码 ...

  9. C#硬件开发业务流程调试技巧

    C#硬件开发,一种是调用厂家提供的api;另一种就是通过com口,发送命令,和硬件通信.这2种方法,如果有硬件,业务流程很好调试.但是大部分硬件,只有和客户联调才会有硬件调试的机会.那业务流程没有硬件 ...

  10. ES6-10笔记(二)

    class类 Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象.但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有class(类) ...