python进阶装饰器_Python进阶: 通过实例详解装饰器(附代码)
Python中的装饰器有很多用处,比如输出日志、参数检查、代理设置、计数计时、结果缓存等等。本文就通过几个装饰器例子,详细解释一下Python中装饰器的用法。一步步从简到繁学习装饰器用法
其他一些装饰器实例
Python中自带的装饰器
按照惯例,先上代码:GitHub - xianhu/LearnPython: 以撸代码的形式学习Python
一步步从简到繁学习装饰器用法
(1)最简单的装饰器,实现日志输出功能:
# 构建装饰器
def logging(func):
@functools.wraps(func)
def decorator():
print("%scalled" % func.__name__)
result = func()
print("%send" % func.__name__)
return result
return decorator
# 使用装饰器
@logging
def test01():
return 1
# 测试用例
print(test01())
print(test01.__name__)
代码很简单,很容易看懂。这里注意"functools.wraps"用法,其目的是"test01.__name__"输出正确的"test01"。"@logging"相当于"test01 = logging(test01)",返回的是decorator函数,所以如果不加"functools.wraps",则"test01.__name__"返回为"decorator"。
注意,此时test01没有参数,对于带有参数的函数,logging装饰器则不再适用。那么如果想装饰带有参数的函数,装饰器该怎么写呢?
(2)装饰器传入函数参数,并正确返回结果:
# 构建装饰器
def logging(func):
@functools.wraps(func)
def decorator(a, b):
print("%scalled" % func.__name__)
result = func(a, b)
print("%send" % func.__name__)
return result
return decorator
# 使用装饰器
@logging
def test01(a, b):
print("in function test01, a=%s, b=%s" % (a, b))
return 1
# 测试用例
print(test01(1, 2))
这里的test01函数带有参数a、b,那么decorator函数带有同样的参数即可。那么问题又来了,如何让logging装饰器更加通用,而不是只装饰参数为两个的函数呢?这时候自然想到Python中的 * 和 ** 的用法。
# 构建装饰器
def logging(func):
@functools.wraps(func)
def decorator(*args, **kwargs):
print("%scalled" % func.__name__)
result = func(*args, **kwargs)
print("%send" % func.__name__)
return result
return decorator
# 使用装饰器
@logging
def test01(a, b):
print("in function test01, a=%s, b=%s" % (a, b))
return 1
# 使用装饰器
@logging
def test02(a, b, c=1):
print("in function test02, a=%s, b=%s, c=%s" % (a, b, c))
return 1
# 测试用例
print(test01(1, 2))
print(test02(1, 2, c=3, d=4))
此时对于任意参数的函数,logging都可以进行装饰。但是注意,logging装饰器是不带参数的,那么装饰器可以带参数吗?当然可以,我们换个例子:参数检查。
(3)构建带有参数的装饰器,并正确返回结果:
# 构建装饰器
def params_chack(a_type, b_type):
def _outer(func):
@functools.wraps(func)
def _inner(a, b):
assert isinstance(a, a_type) and isinstance(b, b_type)
return func(a, b)
return _inner
return _outer
# 使用装饰器
@params_chack(int, (list, tuple))
def test03(a, b):
print("in function test03, a=%s, b=%s" % (a, b))
return 1
# 测试用例
print(test03(1, [2, 3])) # 参数正确
print(test03(1, 2)) # 参数错误
从代码可以看出,实际上就是在原有装饰器的基础上,外层又加了一层包装。params_check装饰器的作用是限制第一个参数为a_type,第二个参数为b_type。类似于(2),这里如何让装饰器更加通用,而不是只装饰参数为两个的函数呢?这里又一次想到Python中的 * 和 **。
# 构建装饰器
def params_chack(*types, **kwtypes):
def _outer(func):
@functools.wraps(func)
def _inner(*args, **kwargs):
result = [isinstance(_param, _type) for _param, _type in zip(args, types)]
assert all(result), "params_chack: invalid parameters"
result = [isinstance(kwargs[_param], kwtypes[_param]) for _param in kwargs if _param in kwtypes]
assert all(result), "params_chack: invalid parameters"
return func(*args, **kwargs)
return _inner
return _outer
# 使用装饰器
@params_chack(int, str, c=(int, str))
def test04(a, b, c):
print("in function test04, a=%s, b=%s, c=%s" % (a, b, c))
return 1
# 测试用例
print(test04(1, "str", 1)) # 参数正确
print(test04(1, "str", "abc")) # 参数正确
print(test04("str", 1, "abc")) # 参数错误
此时params_check装饰器不但能够传入任意个数的参数,而且支持K-V形式的参数传递。
(4)使用装饰器装饰类中的函数,比较简单,直接看代码。注意此时第一个参数为self本身:
# 使用装饰器
class ATest(object):
@params_chack(object, int, str)
def test(self, a, b):
print("in function test of ATest, a=%s, b=%s" % (a, b))
return 1
# 测试用例
a_test = ATest()
a_test.test(1, "str") # 参数正确
a_test.test("str", 1) # 参数错误
(5)多个装饰器同时装饰一个函数,也比较简单,直接看代码:
# 使用装饰器
@logging
@params_chack(int, str, (list, tuple))
def test05(a, b, c):
print("in function test05, a=%s, b=%s, c=%s" % (a, b, c))
return 1
# 测试用例
print(test05(1, "str", [1, 2])) # 参数正确
print(test05(1, "str", (1, 2))) # 参数正确
print(test05(1, "str", "str1str2")) # 参数错误
(6)将装饰器写为类的形式,即“装饰器类”。此时对于装饰器类的要求是必须是可被调用的,即必须实现类的__call__方法。直接上代码:
# 构建装饰器类
class Decorator(object):
def __init__(self, func):
self.func = func
return
def __call__(self, *args, **kwargs):
print("%scalled" % self.func.__name__)
result = self.func(*args, **kwargs)
print("%send" % self.func.__name__)
return result
# 使用装饰器
@Decorator
def test06(a, b, c):
print("in function test06, a=%s, b=%s, c=%s" % (a, b, c))
return 1
# 测试用例
print(test06(1, 2, 3))这里的装饰器类的构造函数中传入func,使其能在__call__方法中被调用。同时这里的装饰器类并没有带有参数,实现不了类似于参数检查的功能。类似于上边的思路,我们这里也可以构建带有参数的装饰器类,还是以参数检查为例:
# 构建装饰器类
class ParamCheck(object):
def __init__(self, *types, **kwtypes):
self.types = types
self.kwtypes = kwtypes
return
def __call__(self, func):
@functools.wraps(func)
def _inner(*args, **kwargs):
result = [isinstance(_param, _type) for _param, _type in zip(args, self.types)]
assert all(result), "params_chack: invalid parameters"
result = [isinstance(kwargs[_param], self.kwtypes[_param]) for _param in kwargs if _param in self.kwtypes]
assert all(result), "params_chack: invalid parameters"
return func(*args, **kwargs)
return _inner
# 使用装饰器
@ParamCheck(int, str, (list, tuple))
def test07(a, b, c):
print("in function test06, a=%s, b=%s, c=%s" % (a, b, c))
return 1
# 测试用例
print(test07(1, "str", [1, 2])) # 参数正确
print(test07(1, "str", (1, 2))) # 参数正确
print(test07(1, 2, (1, 2))) # 参数错误
其他一些装饰器实例
函数缓存:一个函数的执行结果可以被缓存在内存中,下次再次调用时,可以先查看缓存中是否存在,如果存在则直接返回缓存中的结果,否则返回函数调用结果。这种装饰器比较适合装饰过程比较复杂或耗时的函数,比如数据库查询等。
# 实例: 函数缓存
def funccache(func):
cache = {}
@functools.wraps(func)
def _inner(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return _inner
# 使用装饰器
@funccache
def test08(a, b, c):
# 其他复杂或耗时计算
return a + b + c
还有很多其他例子,比如函数调用计数、函数计时、函数自动重试等,思路都基本相同,这里就不一一列举了。
Python中自带的装饰器
Python中自带有三个和class相关的装饰器:@staticmethod、@classmethod 和@property。
(1)先看@property,可以将其理解为“将类方法转化为类属性的装饰器”。先看实例:
# 使用Python自带的装饰器
class People(object):
def __init__(self):
self._name = None
self._age = None
return
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
return
@property
def age(self):
return self._age
@age.setter
def age(self, age):
assert 0 < age < 120
self._age = age
return
p = People()
p.name = "tom" # 设置name
p.age = 12 # 设置age
print(p.name, p.age) # 输出name和age
p.age = 120 # 设置age, 此时认为120为异常数据
这里定义一个People类,有两个属性name和age。当我们声明了实例p,使用p操作name和age时,实际上是调用的name、age方法,此时会做参数检查等工作。@property将name方法转化为属性,同时当对该属性进行赋值时,会自动调用@name.setter将下边的name方法。
@property有.setter、.getter和.deleter三中装饰器,分别对应赋值、取值和删除三种操作。
(2)@staticmethod 将类成员方法声明为类静态方法,类静态方法没有 self 参数,可以通过类名或类实例调用。
(3)@classmethod 将类成员方法声明为类方法,类方法所接收的第一个参数不是self,而是cls,即当前类的具体类型。
静态方法和类方法都比较简单,一个简单的例子解释静态方法和类方法:
# 类静态方法和类方法
class A(object):
var = 1
def func(self):
print(self.var)
return
@staticmethod
def static_func():
print(A.var)
return
@classmethod
def class_func(cls):
print(cls.var)
cls().func()
return
=============================================================
欢迎大家拍砖、提意见。相互交流,共同进步!
==============================================================
python进阶装饰器_Python进阶: 通过实例详解装饰器(附代码)相关推荐
- python命名空间和闭包_Python函数基础实例详解【函数嵌套,命名空间,函数对象,闭包函数等】...
本文实例讲述了Python函数基础用法.分享给大家供大家参考,具体如下: 一.什么是命名关键字参数? 格式: 在*后面参数都是命名关键字参数. 特点: 1.约束函数的调用者必须按照Kye=value的 ...
- python编写文件管理系统_python用户管理系统实例详解
实例讲解python用户管理系统 本文主要为大家分享一篇python用户管理系统的实例讲解,具有很好的参考价值,希望对大家有所帮助.一起跟随小编过来看看吧,希望能帮助到大家. 自定义函数+装饰器,每一 ...
- python元组字典例子_python字典排序实例详解
本文实例分析了python字典排序的方法.分享给大家供大家参考.具体如下: 1. 准备知识: 在python里,字典dictionary是内置的数据类型,是个无序的存储结构,每一元素是key-valu ...
- python装饰器函数-Python函数装饰器常见使用方法实例详解
本文实例讲述了Python函数装饰器常见使用方法.分享给大家供大家参考,具体如下: 一.装饰器 首先,我们要了解到什么是开放封闭式原则? 软件一旦上线后,对修改源代码是封闭的,对功能的扩张是开放的,所 ...
- python中if语句的实例_对python中if语句的真假判断实例详解
说明 在python中,if作为条件语句,当if后面的条件参数为真时,则执行后面的语句块,反之跳过,为了深入理解if语句,我们需要知道if语句的真假判断方式. 示例 在python交互器中,经过测试发 ...
- python判断是否回文_对python判断是否回文数的实例详解
设n是一任意自然数.若将n的各位数字反向排列所得自然数n1与n相等,则称n为一回文数.例如,若n=1234321,则称n为一回文数:但若n=1234567,则n不是回文数. 上面的解释就是说回文数和逆 ...
- python当型循环_对python while循环和双重循环的实例详解
废话不多说,直接上代码吧! #python中,while语句用于循环执行程序,即在某个条件下,循环执行某段程序,以处理需要重复处理的相同任务. #while是"当型"循环结构. i ...
- python画二维散点图-基于python 二维数组及画图的实例详解
1.二维数组取值 注:不管是二维数组,还是一维数组,数组里的数据类型要一模一样,即若是数值型,全为数值型 #二维数组 import numpy as np list1=[[1.73,1.68,1.71 ...
- Python爬虫包 BeautifulSoup 递归抓取实例详解
Python爬虫包 BeautifulSoup 递归抓取实例详解 概要: 爬虫的主要目的就是为了沿着网络抓取需要的内容.它们的本质是一种递归的过程.它们首先需要获得网页的内容,然后分析页面内容并找到另 ...
- python随机生成二维列表_对python产生随机的二维数组实例详解
对python产生随机的二维数组实例详解 最近找遍了python的各个函数发现无法直接生成随机的二维数组,其中包括random()相关的各种方法,都没有得到想要的结果.最后在一篇博客中受到启发,通过列 ...
最新文章
- 逻辑覆盖测试(四)判定/条件覆盖
- SDUT_2122 数据结构实验之链表七:单链表中重复元素的删除
- oracle实例文件,ORACLE实例管理之参数文件
- MATLAB中改变默认当前文件夹
- mysql relaylog 慢_MySQL 主从同步延迟的原因及解决办法
- 浅谈CruiseControl的部署
- python 面向对象_Python 和 Java 基础对比 08 —— 面向对象
- Oracle数据库学习笔记(四)--Oracle体系结构
- 计算机专业云计算论文题目,云计算专业论文题目 云计算论文题目如何定
- 管理信息系统开发流程
- lwip --- (十六)TCP建立流程
- RuoYi若依打包发布与部署
- 手写签名更改为透明背景png图片
- c语言内涵教程练习6,C语言内涵教程练习5参考答案.doc
- 麒麟V10sp1轻松设置开机自启动脚本
- 建设工程法规专科【5】
- ADVPT-C++复习准备
- 光度立体视觉 matlab,Matlab官方光度立体三维成像程序
- 计算机游戏的利与弊议论文,电脑游戏的利与弊800字作文
- 中国手机的“影子计划”
热门文章
- 基于JAVA+SpringMVC+Mybatis+MYSQL的羽毛球场预约管理系统
- FastStone Capture 注册码 序列号
- [转载]linux下上传文件真的需要ftp么?
- imp.load_source的用法
- 微信小程序侧边栏滑动特效(左右滑动)
- [BZOJ] 1614: [Usaco2007 Jan]Telephone Lines架设电话线
- SQL Server 2016/2014/2012/2008/2005/2000简体中文企业版下载地址
- phonegap安装 环境搭建与配置详解(3.4 完整版 提供下载地址)
- [转]PCM文件格式
- SQL数据库语言基础之SqlServer视图的创建、修改与视图数据的增删改查