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进阶: 通过实例详解装饰器(附代码)相关推荐

  1. python命名空间和闭包_Python函数基础实例详解【函数嵌套,命名空间,函数对象,闭包函数等】...

    本文实例讲述了Python函数基础用法.分享给大家供大家参考,具体如下: 一.什么是命名关键字参数? 格式: 在*后面参数都是命名关键字参数. 特点: 1.约束函数的调用者必须按照Kye=value的 ...

  2. python编写文件管理系统_python用户管理系统实例详解

    实例讲解python用户管理系统 本文主要为大家分享一篇python用户管理系统的实例讲解,具有很好的参考价值,希望对大家有所帮助.一起跟随小编过来看看吧,希望能帮助到大家. 自定义函数+装饰器,每一 ...

  3. python元组字典例子_python字典排序实例详解

    本文实例分析了python字典排序的方法.分享给大家供大家参考.具体如下: 1. 准备知识: 在python里,字典dictionary是内置的数据类型,是个无序的存储结构,每一元素是key-valu ...

  4. python装饰器函数-Python函数装饰器常见使用方法实例详解

    本文实例讲述了Python函数装饰器常见使用方法.分享给大家供大家参考,具体如下: 一.装饰器 首先,我们要了解到什么是开放封闭式原则? 软件一旦上线后,对修改源代码是封闭的,对功能的扩张是开放的,所 ...

  5. python中if语句的实例_对python中if语句的真假判断实例详解

    说明 在python中,if作为条件语句,当if后面的条件参数为真时,则执行后面的语句块,反之跳过,为了深入理解if语句,我们需要知道if语句的真假判断方式. 示例 在python交互器中,经过测试发 ...

  6. python判断是否回文_对python判断是否回文数的实例详解

    设n是一任意自然数.若将n的各位数字反向排列所得自然数n1与n相等,则称n为一回文数.例如,若n=1234321,则称n为一回文数:但若n=1234567,则n不是回文数. 上面的解释就是说回文数和逆 ...

  7. python当型循环_对python while循环和双重循环的实例详解

    废话不多说,直接上代码吧! #python中,while语句用于循环执行程序,即在某个条件下,循环执行某段程序,以处理需要重复处理的相同任务. #while是"当型"循环结构. i ...

  8. python画二维散点图-基于python 二维数组及画图的实例详解

    1.二维数组取值 注:不管是二维数组,还是一维数组,数组里的数据类型要一模一样,即若是数值型,全为数值型 #二维数组 import numpy as np list1=[[1.73,1.68,1.71 ...

  9. Python爬虫包 BeautifulSoup 递归抓取实例详解

    Python爬虫包 BeautifulSoup 递归抓取实例详解 概要: 爬虫的主要目的就是为了沿着网络抓取需要的内容.它们的本质是一种递归的过程.它们首先需要获得网页的内容,然后分析页面内容并找到另 ...

  10. python随机生成二维列表_对python产生随机的二维数组实例详解

    对python产生随机的二维数组实例详解 最近找遍了python的各个函数发现无法直接生成随机的二维数组,其中包括random()相关的各种方法,都没有得到想要的结果.最后在一篇博客中受到启发,通过列 ...

最新文章

  1. 逻辑覆盖测试(四)判定/条件覆盖
  2. SDUT_2122 数据结构实验之链表七:单链表中重复元素的删除
  3. oracle实例文件,ORACLE实例管理之参数文件
  4. MATLAB中改变默认当前文件夹
  5. mysql relaylog 慢_MySQL 主从同步延迟的原因及解决办法
  6. 浅谈CruiseControl的部署
  7. python 面向对象_Python 和 Java 基础对比 08 —— 面向对象
  8. Oracle数据库学习笔记(四)--Oracle体系结构
  9. 计算机专业云计算论文题目,云计算专业论文题目 云计算论文题目如何定
  10. 管理信息系统开发流程
  11. lwip --- (十六)TCP建立流程
  12. RuoYi若依打包发布与部署
  13. 手写签名更改为透明背景png图片
  14. c语言内涵教程练习6,C语言内涵教程练习5参考答案.doc
  15. 麒麟V10sp1轻松设置开机自启动脚本
  16. 建设工程法规专科【5】
  17. ADVPT-C++复习准备
  18. 光度立体视觉 matlab,Matlab官方光度立体三维成像程序
  19. 计算机游戏的利与弊议论文,电脑游戏的利与弊800字作文
  20. 中国手机的“影子计划”

热门文章

  1. 基于JAVA+SpringMVC+Mybatis+MYSQL的羽毛球场预约管理系统
  2. FastStone Capture 注册码 序列号
  3. [转载]linux下上传文件真的需要ftp么?
  4. imp.load_source的用法
  5. 微信小程序侧边栏滑动特效(左右滑动)
  6. [BZOJ] 1614: [Usaco2007 Jan]Telephone Lines架设电话线
  7. SQL Server 2016/2014/2012/2008/2005/2000简体中文企业版下载地址
  8. phonegap安装 环境搭建与配置详解(3.4 完整版 提供下载地址)
  9. [转]PCM文件格式
  10. SQL数据库语言基础之SqlServer视图的创建、修改与视图数据的增删改查