Python-装饰器进阶
基本概念
具体概念请先看之前的文章 理解装饰器
装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理, Web权限校验, Cache等。
很有名的例子,就是咖啡,加糖的咖啡,加牛奶的咖啡。本质上,还是咖啡,只是在原有的东西上,做了“装饰”,使之附加一些功能或特性。
例如记录日志,需要对某些函数进行记录
笨的办法,每个函数加入代码,如果代码变了,就悲催了
装饰器的办法,定义一个专门日志记录的装饰器,对需要的函数进行装饰,搞定
优点
抽离出大量函数中与函数功能本身无关的雷同代码并继续重用
即,可以将函数“修饰”为完全不同的行为,可以有效的将业务逻辑正交分解,如用于将权限和身份验证从业务中独立出来
概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能
Python中的装饰器
在Python中,装饰器实现是十分方便的
原因是:函数可以被扔来扔去。
函数作为一个对象:
A.可以被赋值给其他变量,可以作为返回值
B.可以被定义在另外一个函数内
def:
装饰器是一个函数,一个用来包装函数的函数,装饰器在函数申明完成的时候被调用,调用之后返回一个修改之后的函数对象,将其重新赋值原来的标识符,并永久丧失对原始函数对象的访问(申明的函数被换成一个被装饰器装饰过后的函数)
当我们对某个方法应用了装饰方法后, 其实就改变了被装饰函数名称所引用的函数代码块入口点,使其重新指向了由装饰方法所返回的函数入口点。
由此我们可以用decorator改变某个原有函数的功能,添加各种操作,或者完全改变原有实现
分类:
装饰器分为无参数decorator,有参数decorator
* 无参数decorator
生成一个新的装饰器函数
* 有参decorator
有参装饰,装饰函数先处理参数,再生成一个新的装饰器函数,然后对函数进行装饰
装饰器有参/无参,函数有参/无参,组合共4种
具体定义:
decorator方法
A.把要装饰的方法作为输入参数,
B.在函数体内可以进行任意的操作(可以想象其中蕴含的威力强大,会有很多应用场景),
C.只要确保最后返回一个可执行的函数即可(可以是原来的输入参数函数, 或者是一个新函数)
无参数装饰器 – 包装无参数函数
不需要针对参数进行处理和优化
- def decorator(func):
- print "hello"
- return func
- @decorator
- def foo():
- pass
- foo()
foo()等价于:
- foo = decorator(foo)
- foo()
无参数装饰器 – 包装带参数函数
- def decorator_func_args(func):
- def handle_args(*args, **kwargs): #处理传入函数的参数
- print "begin"
- func(*args, **kwargs) #函数调用
- print "end"
- return handle_args
- @decorator_func_args
- def foo2(a, b=2):
- print a, b
- foo2(1)
foo2(1)等价于
- foo2 = decorator_func_args(foo2)
- foo2(1)
带参数装饰器 – 包装无参数函数
- def decorator_with_params(arg_of_decorator):#这里是装饰器的参数
- print arg_of_decorator
- #最终被返回的函数
- def newDecorator(func):
- print func
- return func
- return newDecorator
- @decorator_with_params("deco_args") #注意装饰器参数是这时候使用的
- def foo3():
- pass
- foo3()
与前面的不同在于:比上一层多了一层封装,先传递参数,再传递函数名
第一个函数decomaker是装饰函数,它的参数是用来加强“加强装饰”的。由于此函数并非被装饰的函数对象,所以在内部必须至少创建一个接受被 装饰函数的函数,然后返回这个对象(实际上此时foo3= decorator_with_params(arg_of_decorator)(foo3))
带参数装饰器– 包装带参数函数
- def decorator_whith_params_and_func_args(arg_of_decorator):
- def handle_func(func):
- def handle_args(*args, **kwargs):
- print "begin"
- func(*args, **kwargs)
- print "end"
- print arg_of_decorator, func, args,kwargs
- return handle_args
- return handle_func
- @decorator_whith_params_and_func_args("")
- def foo4(a, b=2):
- print "Content"
- foo4(1, b=3)
实际上此时foo3= decorator_with_params(arg_of_decorator)(foo4)
内置装饰器
内置的装饰器有三个:staticmethod,classmethod, property
- class A():
- @staticmethod
- def test_static():
- print "static"
- def test_normal(self):
- print "normal"
- @classmethod
- def test_class(cls):
- print "class", cls
- a = A()
- A.test_static()
- a.test_static()
- a.test_normal()
- a.test_class()
结果:
- static
- static
- normal
- class __main__.A
A.test_static
staticmethod 类中定义的实例方法变成静态方法
基本上和一个全局函数差不多(不需要传入self,只有一般的参数),只不过可以通过类或类的实例对象来调用,不会隐式地传入任何参数。
类似于静态语言中的静态方法
B.test_normal
普通对象方法:普通对象方法至少需要一个self参数,代表类对象实例
C.test_class
类中定义的实例方法变成类方法
classmethod需要传入类对象,可以通过实例和类对象进行调用。
是和一个class相关的方法,可以通过类或类实例调用,并将该class对象(不是class的实例对象)隐式地当作第一个参数传入。
就这种方法可能会 比较奇怪一点,不过只要你搞清楚了python里class也是个真实地存在于内存中的对象,而不是静态语言中只存在于编译期间的类型,就好办了。正常的 方法就是和一个类的实例对象相关的方法,通过类实例对象进行调用,并将该实例对象隐式地作为第一个参数传入,这个也和其它语言比较像。
D.区别
staticmethod,classmethod相当于全局方法,一般用在抽象类或父类中。一般与具体的类无关。
类方法需要额外的类变量cls,当有子类继承时,调用类方法传入的类变量cls是子类,而不是父类。
类方法和静态方法都可以通过类对象和类的实例对象访问
定义方式,传入的参数,调用方式都不相同。
E.property
对类属性的操作,类似于java中定义getter/setter
- class B():
- def __init__(self):
- self.__prop = 1
- @property
- def prop(self):
- print "call get"
- return self.__prop
- @prop.setter
- def prop(self, value):
- print "call set"
- self.__prop = value
- @prop.deleter
- def prop(self):
- print "call del"
- del self.__prop
其他
A.装饰器的顺序很重要,需要注意
- @A
- @B
- @C
- def f ():
...
等价于
- f = A(B(C(f)))
B.decorator的作用对象可以是模块级的方法或者类方法
C.functools模块提供了两个装饰器。这个模块是Python 2.5后新增的。
functools.wraps(func)total_ordering(cls)这个具体自己去看吧,后续用到了再补充
一个简单例子
通过一个变量,控制调用函数时是否统计时间
- #!/usr/bin/env python
- # -*- coding:utf-8 -*-
- #@author: wklken@yeah.net
- #@version: a test of decorator
- #@date: 20121027
- #@desc: just a test
- import logging
- from time import time
- logger = logging.getLogger()
- logger.setLevel(logging.DEBUG)
- is_debug = True
- def count_time(is_debug):
- def handle_func(func):
- def handle_args(*args, **kwargs):
- if is_debug:
- begin = time()
- func(*args, **kwargs)
- logging.debug( "[" + func.__name__ + "] -> " + str(time() - begin) )
- else:
- func(*args, **kwargs)
- return handle_args
- return handle_func
- def pr():
- for i in range(1,1000000):
- i = i * 2
- print "hello world"
- def test():
- pr()
- @count_time(is_debug)
- def test2():
- pr()
- @count_time(False)
- def test3():
- pr()
- if __name__ == "__main__":
- test()
- test2()
- test3()
结果:
- hello world
- hello world
- DEBUG:root:[test2] -> 0.0748538970947
- hello world
转自:http://blog.csdn.net/wklken/article/details/8118942
参考:http://blog.csdn.net/dreamcoding/article/details/8611578
Python-装饰器进阶的更多相关文章
- (转)python装饰器进阶一
Python装饰器进阶之一 先看例子 网上有很多装饰器的文章,上来说半天也没让人看明白装饰器到底是个什么,究竟有什么用,我们直接来看几个例子. Python递归求斐波那契数列 def fibonacc ...
- Python装饰器进阶
装饰器进阶 现在,我们已经明白了装饰器的原理.接下来,我们还有很多事情需要搞清楚.比如:装饰带参数的函数.多个装饰器同时装饰一个函数.带参数的装饰器和类装饰器. 装饰带参数函数 def foo(fun ...
- (转)python装饰器二
Python装饰器进阶之二 保存被装饰方法的元数据 什么是方法的元数据 举个栗子 def hello(): print('Hello, World.') print(dir(hello)) 结果如下: ...
- Python函数--装饰器进阶
开放封闭原则 1.对扩展是开放的 为什么要对扩展开放呢? 我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改.所以我们必须允许代码扩展.添加新功能. 2.对修改是封 ...
- day 12 - 1 装饰器进阶
装饰器进阶 装饰器的简单回顾 装饰器开发原则:开放封闭原则装饰器的作用:在不改变原函数的调用方式的情况下,在函数的前后添加功能装饰器的本质:闭包函数 装饰器的模式 def wrapper(func): ...
- Python—装饰器详解
装饰器:(语法糖) 本质是函数,它是赋予函数新功能,但是不改变函数的源代码及调用方式 原则: 1.不能修改被装饰函数的源代码 2.不能修改被装饰函数的调用方式 3.函数的返回值也不变 这两点简而言 ...
- day4之装饰器进阶、生成器迭代器
装饰器进阶 带参数的装饰器 # 某一种情况# 500个函数加装饰器, 加完后不想再加这个装饰器, 再过一个季度,又想加上去# 你可以设计你的装饰器,来确认是否执行 # 第一种情况 # 想要500个函数 ...
- 五分钟学会Python装饰器,看完面试不再慌
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是Python专题的第12篇文章,我们来看看Python装饰器. 一段囧事 差不多五年前面试的时候,我就领教过它的重要性.那时候我Pyt ...
- Python 装饰器填坑指南 | 最常见的报错信息、原因和解决方案
本文为霍格沃兹测试学院学员学习笔记. Python 装饰器简介 装饰器(Decorator)是 Python 非常实用的一个语法糖功能.装饰器本质是一种返回值也是函数的函数,可以称之为“函数的函数”. ...
- 关于python装饰器
关于python装饰器,不是系统的介绍,只是说一下某些问题 1 首先了解变量作用于非常重要 2 其次要了解闭包 def logger(func): def inner(*args, **kwargs) ...
随机推荐
- JDBC 编程工具包
工具包结构 工具包代码 BeanCountHander.java import java.sql.ResultSet; public class BeanCountHander implements ...
- [BZOJ2109][NOI2010]航空管制(贪心+拓扑)
2109: [Noi2010]Plane 航空管制 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 1227 Solved: 510[Submit][ ...
- hdu 2955 Robberies(概率背包)
Robberies Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total S ...
- 泛型类Bag
课堂练习--泛型类Bag 要求: 0.代码运行在命令行中,路径要体现学号信息,IDEA中,伪代码要体现个人学号信息 1.参见Bag的UML图,用Java继承BagInterface实现泛型类Bag,并 ...
- Problem F: 零起点学算法85——数组中插入一个数
#include<stdio.h> int main() { ],b[]; while(scanf("%d",&n)!=EOF) { ;i<n;i++) ...
- [典型漏洞分享]结合YS业务分析使用oauth协议的风险
结合YS业务分析oauth协议风险 问题描述: YS 使用QQ互联的openAPI实现QQ登录YS的功能,使用该功能需要在腾讯注册登录时的回调地址,根据oauth协议,用户的code或者access_ ...
- webpack与browser-sync热更新原理深度讲解
本文首发于CSDN网站,下面的版本又经过进一步的修订.原文:webpack与browser-sync热更新原理深度讲解本文包含如下内容: webpack-hot-middleware EventSou ...
- BabyLinux制作过程详解
转:http://www.360doc.com/content/05/0915/14/1429_12641.shtml BabyLinux制作过程详解 作者:GuCuiwen email:win2li ...
- Delphi 对象模型学习笔记(转)
摘要 Borland Object Pascal 对象模型(现在已经正是命名为 Delphi 语言)与其他 OOP 语言一样,都提供了一些基础服务: 如对象创建服务.对象释放服务.对象识别服务 ...
- Solr6.6 IK 中文分词的配置和使用
1. 首先,下载IKAnalyzer ,下载 2. 将ik的相关文件 拷贝到 webapps\solr\WEB-INF\lib 目录下 或者存放solr-6.6.0\server\solr\test\ ...