行走的蓑衣客

导航

 

一、概念

  原文链接: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 装饰器

装饰器:

  1. 关键字:@,在被修饰的函数的前一行加入
  2. 本质:装饰器的本质就是一个函数
  3. 原理:在调用被装饰的函数时,被装饰的函数体的代码并不会被直接执行。而是在调用被装饰的函数时,将该函数传递给装饰器

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()
posted on 2021-08-31 14:01  行走的蓑衣客  阅读(60)  评论(0编辑  收藏  举报