python高级 之(二) --- 类装饰器
装饰器-初级
在不改变原有函数逻辑功能的基础上,为函数添加新的逻辑功能。使代码可读性更高、结构更加清晰、冗余度更低
- 简介
- """
- 闭包: 函数嵌套的格式就是闭包。写装饰器的闭包是外层函数的返回值就是内层函数
- 装饰器:一种特殊的闭包 加上 语法糖[@语法]组成的
- 其作用:在不修改原有功能的基础上,为该功能添加其他新的需求。不管在函数中添加多少装饰,函数的名字没有变化
- 在函数中的使用
- 装饰器的特殊闭包:在外层函数中声明一个内层函数,外等函数的返回值是内层函数
- 外层函数的参数是:func 接受是给哪一个函数添加装饰
- def out(func): 给nomal函数装饰时,此时 func === nomal() | 给diff函数装饰时,此时 func === diff()
- def inner():
- pass 装饰器的新增功能是 写在内层函数中的,写在外层函数没有效果
- return inner
- 需添加装饰的函数:在普通函数添加装饰器的语法糖,@特殊闭包的外层函数名
- @out
- def nomal():
- pass
- @out
- def diff()
- pass
- """
- # 特殊的闭包
- def outter():
- print("这是一个外部函数")
- def inner():
- # 装饰器的新增功能是 写在内层函数中的
- print("这是一个内部函数")
- return inner # 外部函数的返回值是内部函数
- res = outter()
- print('res =',res) # res指向的是内部函数的内存地址。
- # 执行结果:这是一个外部函数 res = <function outter.<locals>.inner at 0x1027e88c8>
- res()
- # 执行结果 :这是一个内部函数。 调用res函数等价于 在outter内部直接调用innner函数
- """======================================================================"""
- 实例
- # 在不改变原有函数的基础上,为函数增加新的功能。
- # 装饰器的闭包,实现功能是:新增计算该功能函数的执行时间
- def get_time(func): # func的作用是 明确为哪一个函数添加装饰器;为哪个函数添加就代表哪一个函数
- def inner():
- import time
- start_time = time.time()
- # 执行被装饰的功能函数
- func()
- end_time = time.time()
- print("消耗的时间为:",end_time - start_time)
- return inner
- @get_time
- def print_table():
- for row in range(1,10):
- for col in range(1,row + 1):
- print(col, "*" ,row, "=", row*col, end="\t")
- print()
- # 不管给该功能函数添加了多少装饰,执行的时候还是调用该函数的名字
- print_table()
- """
- 1 * 1 = 1
- 1 * 2 = 2 2 * 2 = 4
- 1 * 3 = 3 2 * 3 = 6 3 * 3 = 9
- 1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16
- 1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25
- 1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36
- 1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49
- 1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64
- 1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81
- 消耗的时间为: 0.0011630058288574219
- """
- # 在不改变原有函数的基础上,为函数增加新的功能。
- # 装饰器的闭包,实现功能是:新增计算该功能函数的执行时间
- def get_time(func): # func的作用是明确给哪一个函数添加装饰器
- def inner():
- import time
- start_time = time.time()
- # 执行被装饰的功能函数
- func()
- end_time = time.time()
- print("消耗的时间为:",end_time - start_time)
- return inner
- """九九乘法表"""
- @get_time
- def print_table():
- for row in range(1,10):
- for col in range(1,row + 1):
- print(col, "*" ,row, "=", row*col, end="\t")
- print()
- # 不管给该功能函数添加了多少装饰,执行的时候还是调用该函数的名字
- print_table()
- """九九加法表"""
- @get_time
- def print_add_table():
- for row in range(1,10):
- for col in range(1,row + 1):
- print(col, "+" ,row, "=", row+col, end="\t")
- print()
- print_add_table()
多个函数添加同一个装饰器
装饰器的执行流程
- 基本流程
- def get_time(func): # func的作用是明确给哪一个函数添加装饰器
- print("这是装饰器的外层函数。在添加@语法糖时执行,且只会被执行一次")
- def inner():
- import time
- start_time = time.time()
- func() # 执行被装饰的功能函数
- end_time = time.time()
- print("消耗的时间为:",end_time - start_time)
- return inner
- """九九加法表"""
- @get_time
- def print_add_table():
- for row in range(1,10):
- for col in range(1,row + 1):
- print(col, "+" ,row, "=", row+col, end="\t")
- print()
- print_add_table()
- print_add_table() # 无论调用几次被装饰的函数,装饰器的外层函数只会被执行一次
- """
- 为什么在装饰器中增加的新功能要在内层函数中添加?
- 因为在装饰器函数中 无论调用几次被装饰的函数,
- 装饰器外层函数的语句只会被执行一次,
- 第二次调用被装饰函数时不会执行装饰器外层函数语句
- 当语法糖添加装饰器的时候,内部执行流程:
- 1。当使用语法糖给当前函数添加装饰器的时候[添加@时],装饰器特殊闭包的外层函数就已经被调用执行了
- 注意:外层函数的执行会有一个结果的返回,这个结果就是其中的内层函数
- 2。为哪一个功能函数添加的装饰器 就使用该功能函数名字作为变量名,用于接收外层函数执行的返回结果(即内层函数)
- 此时 print_add_table = get_time(print_add_table) # 装饰器函数的参数 是 被装饰的函数
- 3。现在此时 print_add_table 代表的是:装饰器特殊闭包中的内层函数;装饰器的参数func 代表的是被装饰的方法函数
- """
- 装饰器的完善
- def get_time(func): # func的作用是明确给哪一个函数添加装饰器
- def inner(*values,**kwargs): # 带参数的功能函数添加装饰器 所传的值
- import time
- start_time = time.time()
- res = func(*values,**kwargs) # 执行被装饰的功能函数; values 传的参数
- end_time = time.time()
- print("消耗的时间为:",end_time - start_time)
- return res
- return inner
- """
- 计算两个数求和功能的执行时间。即带参数的功能函数 添加装饰器
- """
- @get_time
- def add(a,b):
- return a + b
- res = add(12,34)
- print(res)
- def one_oper(func):
- flag = False # 设置一个标记位。让装饰器只能返回一次结果
- def inner(*value,**kwargs):
- nonlocal flag
- if not flag:
- res = func(*value,**kwargs)
- flag = True
- return res
- return None
- return inner
- @one_oper
- def add(a,b):
- return a + b
- res = add(12,34)
- print(res) # 结果:46
- res = add(34,12)
- print(res) # 结果:None
唯一输出
多个装饰器的执行流程
- 代码
- """
- 一个装饰器可以装饰多个功能函数
- 一个功能函数可以被多个装饰器装饰 执行顺序采用就近原则执行[先执行距离被装饰函数近的]
- """
- def outer1(func):
- print("这是第一个装饰器的外部函数")
- def inner1(*value,**kwargs):
- print("第一个装饰器的内部函数")
- res = func(*value,**kwargs)
- if res != None:
- return res
- return inner1
- def outer2(func):
- print("这是第二个装饰器的外部函数")
- def inner2(*value,**kwargs):
- print("第二个装饰器的内部函数")
- res = func(*value,**kwargs)
- if res != None:
- return res
- return inner2
- def outer3(func):
- print("这是第三个装饰器的外部函数")
- def inner3(*value,**kwargs):
- print("第三个装饰器的内部函数")
- res = func(*value,**kwargs)
- if res != None:
- return res
- return inner3
- @outer1
- @outer2
- @outer3
- def show():
- print("演示添加多个装饰器的功能")
- # 被添加装饰器的功能函数只会被执行一次,而且是在最后才执行
- show()
- 结果
- """
- 执行结果:
- 这是第三个装饰器的外部函数
- 这是第二个装饰器的外部函数
- 这是第一个装饰器的外部函数
- 第一个装饰器的内部函数
- 第二个装饰器的内部函数
- 第三个装饰器的内部函数
- 演示添加多个装饰器的功能
- 执行流程:
- @outer3 此时执行outer3外层函数
- ---> show = outer3(show)
- show === inner3此内部函数
- @outer2 此时执行outer2外层函数
- ---> show = outer2(show)[注意:执行上一个装饰器的时候,show已经被更改数据了]
- 此时型参func=show=innner3 当作实际参数传递给show ===> 即上一个inner3
- 这个时候被赋值的变量show又拥有了新值 inner2
- @outer1
- ---> show = outer1(show)[注意:执行上一个装饰器的时候,show已经被更改数据了]
- 此时型参func=show=innner2 当作实际参数传递给show ===> 即上一个inner2
- 这个时候被赋值的变量show又拥有了新值 inner1
- 所有的装饰器走完之后,此时的show === inner1
- inner1执行完后,此时的show === inner2
- inner2执行完后,此时的show === inner3
- inner3执行完后,此时的show === 被装饰的功能函数
- """
装饰器-进阶
带参数的函数装饰器
之前的例子中装饰器是不能接收参数的,其用法只能适用于一些简单的场景。不传参的装饰器,只能对被装饰函数执行固定逻辑。
装饰器本身就是一个函数,不传参数的装饰器,只能对被装饰的函数执行固定的逻辑,会大大限制其能力。
若要用两个内容大体一致,但是某些地方不同的逻辑,若不能传参数的话,就需要写两个装饰器。
- # 装饰器实现传参需要两层嵌套
- def say_hello(contry): # 外层函数的型参 接收 装饰器传来的实参
- def wrapper(func): # 中层函数的型参 接收 被添加装饰器函数
- def deco(*args, **kwargs): # 内层函数的型参 接收 被添加装饰器的带参数功能函数所传的实参
- if contry == "china":
- print("你好!")
- elif contry == "america":
- print('hello.')
- else:
- return
- func(*args, **kwargs) # 真正执行函数的地方。
- return deco
- return wrapper
- @say_hello("america")
- def american():
- print("I am from America。")
- @say_hello("china")
- def chinese():
- print("我来自中国。")
- american()
- chinese()
- """
- 结果:
- hello.
- I am from America。
- 你好!
- 我来自中国。
- """
装饰器-高级
不带参数的类装饰器
- """
- 基于类实现的装饰器,需要两个类方法来实现
- __init__ : 接收被装饰的函数
- __call__ : 实现装饰器的逻辑
- """
- class Text(object):
- def __init__(self, func): # 接收被装饰的函数
- self.func = func
- def __call__(self, *args, **kwargs): # 实现装饰器的逻辑
- print("正在被装饰的函数是:{func}".format(func=self.func.__name__))
- return self.func(*args, **kwargs)
- @Text
- def func(something):
- print("函数的参数是:{}!".format(something))
- func("hello")
- """
- 结果:
- 正在被装饰的函数是:func
- 函数的参数是:hello!
- """
带有参数的类装饰器
- """
- 带参数的类装饰器
- __init__ :不再接收被装饰的函数,而是接收传入的参数。
- __call__ :接收被装饰函数,实现装饰逻辑。
- """
- class logger(object):
- def __init__(self, level='INFO'): # 接收装饰器传入的参数
- self.level = level
- def __call__(self, func): # 接受被装饰的函数
- def wrapper(*args, **kwargs):
- print("[{level}]: the function {func}() is running..."\
- .format(level=self.level, func=func.__name__))
- func(*args, **kwargs) # 真正执行被装饰的函数的语句
- return wrapper # 返回内层函数
- @logger(level='WARNING')
- def say(something):
- print("say {}!".format(something))
- say("hello")
- @logger(level='DEBUG')
- def say(something):
- print("say {}!".format(something))
- say("hello")
- """
- 结果:
- [WARNING]: the function say() is running...
- say hello!
- [DEBUG]: the function say() is running...
- say hello!
- """
系统提供的装饰器
Python语言本身提供的property内建装饰器,通常存在于类中的方法属性化。详情见 类的方法属性化 笔记整理
- """
- Python语言本身提供的property内建装饰器,通常存在于类中的方法属性化
- 可以将一个方法函数定义成一个属性,属性的值就是该函数return的内容。
- """
- # 用@property装饰过的函数,会将一个函数定义成一个属性,属性的值就是该函数return的内容。即方法属性化
- class Student(object):
- def __init__(self, name):
- self.name = name
- self.name = None
- @property
- def age(self):
- return self._age
- @age.setter
- def age(self, value):
- if not isinstance(value, int):
- raise ValueError('输入不合法:年龄必须为数值!')
- if not 0 < value < 100:
- raise ValueError('输入不合法:年龄范围必须0-100')
- self._age=value
- @age.deleter
- def age(self):
- del self._age
- XiaoMing = Student("小明")
- # 设置属性
- XiaoMing.age = 25
- # 查询属性
- age = XiaoMing.age
- print(age)
- # 删除属性。删除后再去查询该属性就会报错
- del XiaoMing.age
- age = XiaoMing.age
- print(age)
python高级 之(二) --- 类装饰器的更多相关文章
- [b0019] python 归纳 (五)_类装饰器
总结: 类装饰器, 本质是一个函数,输入一个类,返回一个类 Case 1 啥都没做 def deco(in_class): return in_class @deco class Cat: def _ ...
- python带参数的类装饰器
# -*- coding: utf-8 -*- # author:baoshan # 带参数的类装饰器(和不带参数的类装饰器有很大的不同) # 类装饰器的实现,必须实现__call__和__init_ ...
- 接口测试基础——第7篇 简单的Python知识普及(二)之装饰器
今天我们来学习python里面的“装饰器” 1.我在函数test运行前想先打印一些内容怎么办? def func(param): print u"打印的内容" para ...
- 【Python学习之二】装饰器
装饰器 首先,给出装饰器的框架: def log(func): def wrapper(*args, **kw): print('call %s():' % func.__name__) return ...
- python 装饰器(五):装饰器实例(二)类装饰器(类装饰器装饰函数)
回到装饰器上的概念上来,装饰器要求接受一个callable对象,并返回一个callable对象(不太严谨,详见后文). 那么用类来实现也是也可以的.我们可以让类的构造函数__init__()接受一个函 ...
- python基础整理4——面向对象装饰器惰性器及高级模块
面向对象编程 面向过程:根据业务逻辑从上到下写代码 面向对象:将数据与函数绑定到一起,进行封装,这样能够更快速的开发程序,减少了重复代码的重写过程 面向对象编程(Object Oriented Pro ...
- python 描述符 上下文管理协议 类装饰器 property metaclass
1.描述符 #!/usr/bin/python env # coding=utf-8 # 数据描述符__get__ __set__ __delete__ ''' 描述符总结 描述符是可以实现大部分py ...
- Python进阶内容(二)--- 装饰器
谈装饰器前,需要明白一件事,Python 中的函数和 Java.C++不太一样,Python 中的函数可以像普通变量一样当做参数传递给另外一个函数,例如: def foo(): print(" ...
- 详解Python闭包,装饰器及类装饰器
在项目开发中,总会遇到在原代码的基础上添加额外的功能模块,原有的代码也许是很久以前所写,为了添加新功能的代码块,您一般还得重新熟悉源代码,稍微搞清楚一点它的逻辑,这无疑是一件特别头疼的事情.今天我们介 ...
随机推荐
- Android Studio Terminal 常用命令
一.Manifest merger failed with multiple errors, see logs 在android开发的时候,有时候会遇到这样的问题 Error:Execution fa ...
- Redis主从复制配置+哨兵模式
架构设计: master:s0 slave:s1.s2 主机映射信息如下: 192.168.32.100 s0 192.168.32.101 s1 192.168.32.102 s2 1.安装Redi ...
- C语言1-2019秋第一周作业
第一周作业 1.你对软件工程专业或者计算机科学与技术专业了解是怎样? 软件技术专业是计算机科学的一个分支,和软件科学与技术专业相比较,软件技术专业的侧重点在开发和技术的实际应用,而对软件开发的理论要求 ...
- PHP Swoole websocket协议实现
- BZOJ 2219 数论之神 (CRT推论+BSGS+原根指标)
看了Po神的题解一下子就懂了A了! 不过Po神的代码出锅了-solve中"d-temp"并没有什么用QwQQwQQwQ-应该把模数除以p^temp次方才行. 来自BZOJ讨论板的h ...
- html2canvas 使用指南
html2canvas(document.body).then(function(canvas) { document.body.appendChild(canvas); }); 属性参数: http ...
- Makefile简单编写实例
介绍一下Makefile的简单编写例子. 编写Makefile的规则就是: 目标文件:依赖文件 (tab)编译规则 现在我有一个文件目录结构为: 解释一下这几个文件.首先我创建makefile目录,底 ...
- Spring Boot教程(三十九)使用MyBatis注解配置详解(2)
增删改查 MyBatis针对不同的数据库操作分别提供了不同的注解来进行配置,在之前的示例中演示了@Insert,下面针对User表做一组最基本的增删改查作为示例: public interface U ...
- 分享代码到GitHub
1.本地安装git 2.项目文件夹右键Git bash here,弹出git命令窗口 3.输入git init,使项目加入Git管理 4.输入git add .,将项目全部内容添加到git 5.输入“ ...
- Hadoop-2.7.5完全分布式搭建
1.在虚拟机上安装Hadoop完全分布式准备工作 1)这里使用的是VMWare软件,在VMWare上安装一个CentOS6.5,并再克隆两个机器配置相关MAC地址,以及配置机器名 2)三台虚拟机配置好 ...