装饰器-初级

  在不改变原有函数逻辑功能的基础上,为函数添加新的逻辑功能。使代码可读性更高、结构更加清晰、冗余度更低

  • 简介
 """
闭包: 函数嵌套的格式就是闭包。写装饰器的闭包是外层函数的返回值就是内层函数
装饰器:一种特殊的闭包 加上 语法糖[@语法]组成的
其作用:在不修改原有功能的基础上,为该功能添加其他新的需求。不管在函数中添加多少装饰,函数的名字没有变化 在函数中的使用
装饰器的特殊闭包:在外层函数中声明一个内层函数,外等函数的返回值是内层函数
外层函数的参数是: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高级 之(二) --- 类装饰器的更多相关文章

  1. [b0019] python 归纳 (五)_类装饰器

    总结: 类装饰器, 本质是一个函数,输入一个类,返回一个类 Case 1 啥都没做 def deco(in_class): return in_class @deco class Cat: def _ ...

  2. python带参数的类装饰器

    # -*- coding: utf-8 -*- # author:baoshan # 带参数的类装饰器(和不带参数的类装饰器有很大的不同) # 类装饰器的实现,必须实现__call__和__init_ ...

  3. 接口测试基础——第7篇 简单的Python知识普及(二)之装饰器

    今天我们来学习python里面的“装饰器” 1.我在函数test运行前想先打印一些内容怎么办? def func(param):    print u"打印的内容"    para ...

  4. 【Python学习之二】装饰器

    装饰器 首先,给出装饰器的框架: def log(func): def wrapper(*args, **kw): print('call %s():' % func.__name__) return ...

  5. python 装饰器(五):装饰器实例(二)类装饰器(类装饰器装饰函数)

    回到装饰器上的概念上来,装饰器要求接受一个callable对象,并返回一个callable对象(不太严谨,详见后文). 那么用类来实现也是也可以的.我们可以让类的构造函数__init__()接受一个函 ...

  6. python基础整理4——面向对象装饰器惰性器及高级模块

    面向对象编程 面向过程:根据业务逻辑从上到下写代码 面向对象:将数据与函数绑定到一起,进行封装,这样能够更快速的开发程序,减少了重复代码的重写过程 面向对象编程(Object Oriented Pro ...

  7. python 描述符 上下文管理协议 类装饰器 property metaclass

    1.描述符 #!/usr/bin/python env # coding=utf-8 # 数据描述符__get__ __set__ __delete__ ''' 描述符总结 描述符是可以实现大部分py ...

  8. Python进阶内容(二)--- 装饰器

    谈装饰器前,需要明白一件事,Python 中的函数和 Java.C++不太一样,Python 中的函数可以像普通变量一样当做参数传递给另外一个函数,例如: def foo(): print(" ...

  9. 详解Python闭包,装饰器及类装饰器

    在项目开发中,总会遇到在原代码的基础上添加额外的功能模块,原有的代码也许是很久以前所写,为了添加新功能的代码块,您一般还得重新熟悉源代码,稍微搞清楚一点它的逻辑,这无疑是一件特别头疼的事情.今天我们介 ...

随机推荐

  1. C++ vector 实例二

    // constructing vectors #include <iostream> #include <vector> int main () { // construct ...

  2. 201871010126 王亚涛 《面向对象程序设计 (Java)》第十七周学习总结

    内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p/12 ...

  3. OkHttp3-基本用法(转)

    OkHttp 一个支持Http和Http/2,可适用于Android以及Java应用的网络请求客户端. 概述 Http是现代网络应用的所常用的协议,它是一种数据传输的媒介.执行高效的Http代码可以让 ...

  4. Laravel进行数据库迁移(migration)

    迁移(migration) 文档的简介是:迁移就像数据库的版本控制,允许团队简单轻松的编辑并共享应用的数据库表结构,迁移通常和 Laravel 的结构构建器结对从而可以很容易地构建应用的数据库表结构. ...

  5. 【agc002f】Leftmost Ball

    题目大意 有n种颜色,每种k个球.将这些球任意排列,将每种颜色中最前面的一个求涂成白色(就是n+1种颜色),求最终的排列的方案的个数. 解题思路 考虑如何计算不会算重, 按颜色顺序,每次往排列插入k个 ...

  6. hdu 6065 RXD, tree and sequence

    题 OwO http://acm.hdu.edu.cn/showproblem.php?pid=6065 (2017 Multi-University Training Contest - Team ...

  7. nginx信号

    ps aux | grep nginx #查看nginx进程,要查看nginx的pid,就得使用这个命令查看*********************nginx信号****************** ...

  8. jquery unload方法 语法

    jquery unload方法 语法 作用:当用户离开页面时,会发生 unload 事件.具体来说,当发生以下情况时,会发出 unload 事件:点击某个离开页面的链接在地址栏中键入了新的 URL使用 ...

  9. php文件夹上传

    php上传大文件配置 PHP用超级全局变量数组$_FILES来记录文件上传相关信息的. 1.file_uploads=on/off 是否允许通过http方式上传文件 2.max_execution_t ...

  10. 20190716NOIP模拟赛T2 通讯(tarjan缩点+贪心)

    题目描述 “这一切都是命运石之门的选择.” 试图研制时间机器的机关SERN截获了中二科学家伦太郎发往过去的一条短 信,并由此得知了伦太郎制作出了电话微波炉(仮). 为了掌握时间机器的技术,SERN总部 ...