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闭包,装饰器及类装饰器
在项目开发中,总会遇到在原代码的基础上添加额外的功能模块,原有的代码也许是很久以前所写,为了添加新功能的代码块,您一般还得重新熟悉源代码,稍微搞清楚一点它的逻辑,这无疑是一件特别头疼的事情.今天我们介 ...
随机推荐
- php类知识---最疯狂的魔术方法serialize,_sleep,__wakeup,unserialize,__autoload,__clone
serialize-----把实例化的对象写入文件 __sleep 调用serialize时触发 <?php class mycoach { public function __construc ...
- 01_3大配置管理工具、SaltStack安装、修改minion_id
1.配置管理 1.1 puppet /'pʌpɪt/ 木偶:傀儡:受他人操纵的人 使用自有的puppet描述语言,可管理配置文件.用户.cron任务.软件包.系统服务等. 问题: 学习曲线非常陡峭 ...
- Centos7搭建CDH6.0.1(单机版)
一.前言. 学习大数据组件,最好的方式是直接参照官网.不过官网的教程也让我吃了一坑,在此记录一下.因在个人笔记本资源有限,在此安装为单机版安装 二.搭建. 1.1配置主机名 hostnamectl s ...
- MessagePack Java Jackson Dataformat - 安装
中文标题[MessagePack 的 Jackson 数据格式] 本页面中的所有示例程序,请到代码库 https://github.com/cwiki-us-demo/serialize-deseri ...
- 数位dp入门(内容一样,新版格式)
顾名思义,数位dp,是一种用来计数的dp,就是把一个数字拆成一个一个数位去统计 如果现在给你一道题,需要你求在区间[l,r]内满足条件的解的个数,我们很容易想到去暴力枚举,但要是数据范围太大这种办法就 ...
- Catch That Cow (POJ - 3278)(简单BFS)
转载请注明出处:https://blog.csdn.net/Mercury_Lc/article/details/82693928作者:Mercury_Lc 题目链接 题解:给你x.y,x可以加1.减 ...
- 推荐系统系列(四):PNN理论与实践
背景 上一篇文章介绍了FNN [2],在FM的基础上引入了DNN对特征进行高阶组合提高模型表现.但FNN并不是完美的,针对FNN的缺点上交与UCL于2016年联合提出一种新的改进模型PNN(Produ ...
- Android中关于回调概念的笔记
一.回调函数 回调函数就是一个通过函数指针调用的函数.如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数.回调函数不是由该函数的实现方直接调 ...
- AtCoder AGC005E Sugigma: The Showdown (博弈论)
题目链接 https://atcoder.jp/contests/agc005/tasks/agc005_e 题解 完了真的啥都不会了-- 首先,显然如果某条A树的边对应B树上的距离大于等于\(3\) ...
- hbase基于hue的查询语法
hbase基于hue的查询语法 登录地址 https://hue-ui.xiaoniangao.cn 界面操作说明 进入hue中的hbase 进入表的查询界面 界面说明 查询语句 ,表示结束查询,可以 ...