一、概念
原文链接:https://blog.csdn.net/qq_54730385/article/details/114393236
装饰器是一种设计模式,经常用来实现"面向切面的编程"(AOP: 实现在不修改源代码的情况下,给程序动态添加功能的一种技术)。装饰器的作用:装饰器允许向一个现有的对象(函数)添加新的功能,同时又不改变其结构,可以抽离出大量的函数中的和业务无关的功能。应用场景:插入日志、性能测试、事务处理、缓存、中间件、权限控制等。
实例:
现在需要计算某个函数的执行时间
import time def fun1(): start = time.time() s = 0 for i in range(1, 100001): s += i print(f'和为:{s}') t = time.time() - start print(f'函数的执行时间为:{t:.10f}') fun1() def fun2(): start = time.time() s = 1 for i in range(1, 100001): s *= i print(f'乘积为:{s}') t = time.time() - start print(f'函数的执行时间为:{t:.10f}') fun2()
如果要把计算时间的代码抽离出来,此时就可以使用装饰器来实现
import time def get_time(func): def wrapper(): start = time.time() func() t = time.time() - start print(f'函数的执行时间为:{t}') return wrapper @get_time def fun2(): s = 1 for i in range(1, 100001): s *= i print(f'乘积为:{s}') fun2()
二、装饰器详解
1 装饰器
装饰器:
- 关键字:@,在被修饰的函数的前一行加入
- 本质:装饰器的本质就是一个函数
- 原理:在调用被装饰的函数时,被装饰的函数体的代码并不会被直接执行。而是在调用被装饰的函数时,将该函数传递给装饰器
2 装饰器的基本形式
# 装饰器函数必须要有一个参数,来接收被装饰的函数(名) def my_decoration(func): #print('这里写要装饰的东西,要给被装饰的函数添加的功能') print('*'*10,'我是华丽的分隔线','*'*10) return func @my_decoration def f(): print('这是一个函数') # 这里调用被装饰的函数时,实际上先调用了装饰器,将函数本身 # 传递给装饰器函数,然后执行装饰器函数内部的代码 f() @my_decoration def f2(): print('这是另一个函数') f2()
3 装饰器-内嵌函数
def my_decoration(func): def wrapper(): # 这里和上述的基本形式本质上是一样 print('这是要装饰的内容') wrapper() return func @my_decoration def f2(): print('这是另一个函数') f2()
4 装饰器-闭包函数
def my_decoration(func): def wrapper(): print('\n'+'*'*10,'start','*'*10) func() # 这是调用被装饰的函数 print('*'*11,'end','*'*11,'\n') return wrapper @my_decoration def f(): print('这是另一个函数') f()
5 装饰器闭包原理剖析
# 闭包:内函数引用了外函数的局部变量,并且外函数返回了内函数对象本身 def outer(x): def inner(): return x return inner ot = outer('哈哈哈') print(ot())
# 在上述的代码中,给外函数传递的是一个字符串类型的参数,其实也可给外函数传递一个函数对象 def outer(x): def inner(): x() # 这里实际是调用了f1()函数 return inner def f1(): print('这是f1函数') ot = outer(f1) ot()
# @语法糖 def outer(func): def inner(): print(1111111111) func() # 这里实际是调用了f1()函数 print(22222222) return inner @outer # 2. 这是Python的装饰器的语法糖 def f2(): print('这是f2函数') f2()
三、带参数的装饰器
之前实现的装饰器,给被装饰的函数添加的都是相同的功能,如果希望这个装饰对不同的函数作出不同的响应,此时就需要给装饰器传参数,在装饰器的内部根据参数的不同,作出不同的操作
def my_decoration(a): def wrapper(func): def inner(): if a < 10: print(1111) else: print(2222) func() return inner return wrapper @my_decoration(a=5) def f1(): print('这是第一个函数') f1() @my_decoration(a=20) def f2(): print('这是第二个函数') f2()
四、类装饰器
装饰器不一定只能用函数来实现,也可以使用类来装饰,用法与函数装饰器区别不大,实质上是调用了类方法中__call__魔法方法
class logging: def __init__(self, func): print('__init__',func) self.__func = func def __call__(self): print(1111111) return self.__func() @logging def hello(): print('hello 你好呀') hello() # 调用了类装饰器中魔法方法 __call__ 敲敲就会了
五、内置装饰器
Python语言本身也有一些装饰器,比如@property
class Person: def __init__(self,name,age): self.name = name self.age = age gou = Person('二狗',18) gou.age = 20 # 1. 属性暴露 2. 可以外界随意更改
修改:
class Person: def __init__(self, name, age): self.__name = name self.__age = age def set_age(self, age): if isinstance(age, int): if 0 < age < 100: self.__age = age else: raise ValueError('年龄超出范围') else: raise TypeError('年龄类型错误') def get_age(self): return self.__age gou = Person('二狗', 18) gou.set_age('abc') # TypeError: 年龄类型错误 print(gou.get_age())
再次修改:
class Person: 同上.... age = property(fget=get_age, fset=set_age) gou = Person('二狗', 18) gou.age = 200 # ValueError: 年龄超出范围 print(gou.age)
@property
# 使类中的方法可以像属性一样调用 class Person: def __init__(self, name, age): self.__name = name self.__age = age @property def age(self): return self.__age @age.setter def age(self,a): self.__age = a gou = Person('二狗', 18) gou.age = 20 # 调用 setter print(gou.age) # 调用 property
@staticmethod
将类中的方法设置为静态方法,它不需要创建实例对象,就可以使用类名来调用
class Person: x = 100 xxxxxx...... 其它代码自己补 @staticmethod def f(): print(Person.x) # 有一个方法不需要去访问实例属性 print('静态方法') Person.f() # 可以直接用类名调用 p = Person('Tom',18) p.f() # 也可以用对象调用
@classmethod
class Person: xxxxxxxxxxxxx.............. @staticmethod def f(): print(Person.x) print('静态方法') @classmethod def n(cls): # 不用写类名 这里的cls就是类名 print(cls,type(cls)) print(isinstance(cls,Person)) print(cls.x) Person.n() p = Person('Tom',18) p.n()