装饰器主要是用来对函数的操作,我们把定义的函数比作一个蛋糕的话,那么装饰器就是盒子,如果要吃蛋糕就先打开盒子。具体到程序中就是在函数外层又套了一层,套的那一层就是一个装饰器。这么说可能有点抽象,那么我们下面就来举例说明.

1 应用场景需求

假设我们有一个程序,里面有N个函数(模块),由于是新上的功能,为了打开市场领导要求所有的功能免费开放,不加任何限制。

def func1():
print("模块1") def func2():
print("模块2")
func1()
func2()

如上,两个功能模块没有任何限制,谁都可以用。

那么现在有问题了,系统上线一段时间之后,公司决定部分限制免费, 部分功能模块需要会员才能访问,其中包括func1,必须登录验证完后才能使用。这个时候我们怎么做呢,我们可能会这么做:

def log_auth(user,passwd):
# 登录验证模块
if user == "aa" and passwd == "bbb":
# 执行验证代码..........
pass
return True def func1():
if log_auth("aaa",""):
print("模块1") def func2():
print("模块2")

增加一个验证模块,然后在需要验证的模块中增加一个判断,如果验证成功就执行模块。

不错,这样做确实可以实现,但是这样做是合理的吗,这样我们就修改了原有的已经上线的代码,这是开发的大忌。那么我们怎么能够实现不动原有代码,又增加这个功能呢。

我们知道函数是可以传递参数的,我们能不能定义一个函数比如outer,将func1这个函数作为一个参数传递进去,直接执行outer就可以在里面增加权限判断后调用函数func1呢。

看一下下面代码:

def outer(func):
# 验证模块
print("验证")
func() def func1():
print("模块1") outer(func1)

以上代码执行后即进行了验证,有执行了原有的函数fuc1,而且原有func1也没有变更。貌似解决了我们的问题,你一定很开心问题解决了。但你想一想问题真的解决了吗?

程序其它地方以前是调用的func1()函数,那我们所有调用这个函数的地方还是调用原函数,要实现必须验证的话原有调用func1的地方是不都需要改为outer(func1)了呢,这样还是改动了原有程序。你可能会想,那就把原来的func1函数的地址指向outer(func1)不就可以了吗,于是你改成了这样

def outer(func):
# 验证模块
print("验证")
func() def func1():
print("模块1") func1 = outer(func1)
func1

哈哈,问题解决! 可是你发现了一个问题, outer(func1)就执行了,你如果把这个赋给func1,其它地方调用的时候都是用func1()来执行的,这样就有问题了。这个时候不知道怎么办了吧。我们看一个例子:

def func1():
print("模块1") print(func1)
结果:<function func1 at 0x00000000037381E0>

我们看到在函数执行时,单func1只是内存中的一个地址,只有执行 func1() 才是执行函数,那么回到上面的问题。 现在 outer(func1)就已经是函数了,你要想使用func1 = auth(func1) ,是不是只要把auth(func1)的结果变成一个内存地址就可以了呢? 也就是将它变成一个函数名,那就得加上def。 基于这一点,我们把上面的改造一下。看下面的代码:

def auth(func):
def outer():
# 验证模块
print("验证")
func()
return outer def func1():
print("模块1") func1 = auth(func1)
func1()

我们把outer()封装在auth()函数里面,让func1 = auth(func1),这样auth(func1)函数执行后返回了一个结果,这个结果就是outer,这个只是个函数名,在内存中只是一个地址。这样func1 = outer的内存地址, 当我们再执行func1()的时候,是不是就相当于是执行了outer()函数。而这个函数就是我们要的东西,这样是不是就实现了我们所要的功能呢。

这样一来真个程序就完美了。哈哈.....可以收工了!!!!!!!

等一下,还有个小问题,如果我们真这么搞得话,是不是每个需要增加验证功能的函数都重新赋值一下:func = auth(func) ???如果我们有200个函数都需要增加验证功能,那是不是要写200个这样的赋值,这样也太麻烦了吧,是不是太low了,有没有更好的办法呢?? 答案是当然有,你能想到这个问题,我们的python开发者难道想不到吗? 我们看这个代码:

def auth(func):
def outer():
# 验证模块
print("验证")
func()
return outer @auth
def func1():
print("模块1") func1()
 

看到了吗,我们只需要在要增加验证的函数上使用@函数名就OK了,这样一来,原来的函数func1没有任何变化,也没有改变其它地方调用func1的调用方式,还增加了验证功能,是不是perfect ???

说了一大堆,我们不是要讲装饰器吗? 难不成这就是装饰器?? 你说对了,这就是装饰器!那你会问了,如果我的func1还有参数怎么搞???

好问题!!看一下下面的代码:

def auth(func):
def outer(para):
# 验证模块
print("验证")
func(para)
return outer @auth
def func1(args):
print("模块1,我的参数:%s " %args) func1("参数来了")

是不是搞定了,很easy了吧。以上就是装饰器的实现。

下面给一个多参数,多层装饰器的例子,原理一样

def log_auth(bef, end):
def outer(func):
def inter(**arg):
print("login successful")
r1 = bef(arg["before"])
r2 = end(arg["end"])
a = func(arg["middle"])
result1 = "{before},{middle},{end}".format(before=r1, middle=a, end=r2)
return result1
return inter
return outer def before(arg1):
return "before:{0}".format(arg1) def later(arg2):
return "later:{0}".format(arg2) @log_auth(before, later)
def index_page(arg):
return "Index {0}".format(arg) print(index_page(before="", middle="", end=""))

Python系列之 - 装饰器的更多相关文章

  1. Noah的学习笔记之Python篇:装饰器

    Noah的学习笔记之Python篇: 1.装饰器 2.函数“可变长参数” 3.命令行解析 注:本文全原创,作者:Noah Zhang  (http://www.cnblogs.com/noahzn/) ...

  2. python设计模式之装饰器详解(三)

    python的装饰器使用是python语言一个非常重要的部分,装饰器是程序设计模式中装饰模式的具体化,python提供了特殊的语法糖可以非常方便的实现装饰模式. 系列文章 python设计模式之单例模 ...

  3. 第7.18节 案例详解:Python类中装饰器@staticmethod定义的静态方法

    第7.18节 案例详解:Python类中装饰器@staticmethod定义的静态方法 上节介绍了Python中类的静态方法,本节将结合案例详细说明相关内容. 一.    案例说明 本节定义了类Sta ...

  4. python高级之装饰器

    python高级之装饰器 本节内容 高阶函数 嵌套函数及闭包 装饰器 装饰器带参数 装饰器的嵌套 functools.wraps模块 递归函数被装饰 1.高阶函数 高阶函数的定义: 满足下面两个条件之 ...

  5. [python基础]关于装饰器

    在面试的时候,被问到装饰器,在用的最多的时候就@classmethod ,@staticmethod,开口胡乱回答想这和C#的static public 关键字是不是一样的,等面试回来一看,哇,原来是 ...

  6. python笔记 - day4-之装饰器

                 python笔记 - day4-之装饰器 需求: 给f1~f100增加个log: def outer(): #定义增加的log print("log") ...

  7. Python深入05 装饰器

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 装饰器(decorator)是一种高级Python语法.装饰器可以对一个函数.方法 ...

  8. Day04 - Python 迭代器、装饰器、软件开发规范

    1. 列表生成式 实现对列表中每个数值都加一 第一种,使用for循环,取列表中的值,值加一后,添加到一空列表中,并将新列表赋值给原列表 >>> a = [0, 1, 2, 3, 4, ...

  9. 第二篇:python高级之装饰器

    python高级之装饰器   python高级之装饰器 本节内容 高阶函数 嵌套函数及闭包 装饰器 装饰器带参数 装饰器的嵌套 functools.wraps模块 递归函数被装饰 1.高阶函数 高阶函 ...

随机推荐

  1. 剑指Offer-字符流中第一个不重复的字符

    package String; import java.util.LinkedHashMap; import java.util.Map; /** * 字符流中第一个不重复的字符 * 题目描述 * 请 ...

  2. 常用的Maven依赖

    一.数据库类型 1.mysql驱动 <!-- mysql驱动支持 --> <dependency> <groupId>mysql</groupId> & ...

  3. linux利用ssh远程执行多台机器执行同样的命令

    这篇文章主要介绍了ssh远程执行命令方法和Shell脚本实例,本文讲解了ssh执行远程操作方法和远程执行命令shell脚本示例,需要的朋友可以参考下 ssh执行远程操作命令格式代码如下: ssh -t ...

  4. linux --> 系统信息命令

    系统信息命令 # uname -a // # 查看内核/操作系统/CPU信息 # head -n /etc/issue // # 查看操作系统版本 # cat /proc/cpuinfo // # 查 ...

  5. Lucene 4.4.0中常用的几个分词器

    一.WhitespaceAnalyzer 以空格作为切词标准,不对语汇单元进行其他规范化处理.很明显这个实用英文,单词之间用空格. 二.SimpleAnalyzer 以非字母符来分割文本信息,并将语汇 ...

  6. JVM活学活用——优化springboot

    介绍 在SpringBoot的Web项目中,默认采用的是内置Tomcat,当然也可以配置支持内置的jetty,内置有什么好处呢? 1. 方便微服务部署. 2. 方便项目启动,不需要下载Tomcat或者 ...

  7. C#,DataHelper,一个通用的帮助类,留个备份。

    using System; using Newtonsoft.Json; using System.IO; using System.Text; namespace CarHailing.Base { ...

  8. java中的异常类型以及区别????

    一.引言 根据JDK的文档我们能够找到异常所在的包:java.lang.Throwable中,Throwable是所有异常类的根类,error是错误,在java.lang.error中,而Except ...

  9. C语言作业第二次总结

    1.作业亮点 1.1作业整体概况 本次作业全体同学能够按时完成作业,且大部分同学的作业体现了自己的思路和方法,具备了一定变成能力. 1.2推荐博客 林岳-代码注释清晰,详细.->博文 王艺斌-算 ...

  10. Java 密码学算法

    Java 密码学算法 候捷老师在< 深入浅出MFC 2e(电子版)>中引用林语堂先生的一句话: 只用一样东西,不明白它的道理,实在不高明 只知道How,不知道Why,出了一点小问题时就无能 ...