【Python】装饰器用法详解(附代码)

作用

在不改变原有函数代码,且保持原函数调用方法不变的情况下,给原函数增加新的功能(或者给类增加属性和方法)

  • 核心思想:用一个函数(或者类)去装饰另一个函数(或者类),造出一个新函数(或者新类)
  • 应用场景:引入日志,函数执行时间的统计,执行函数前的准备工作,执行函数后的处理工作,权限校验,缓存等
  • 语法规则:在原有的函数上加上 @符,装饰器会把下面的函数当作参数传递到装饰器中,@符又被成为 语法糖

装饰器原型

# 利用闭包函数把函数当作参数传递,并且在函数内去调用传递进来的函数,并返回一个函数
def outer(f):

    def inner():
        print('我在函数func之前被执行')
        f()
        print('我在函数func之后被执行')

    return inner


@outer
def func():
    print('我是函数func')

func()

输出:
在这里插入图片描述

装饰器装饰函数

无参数函数

import time


def runtime(f):

    def inner():
        start = time.perf_counter()
        f()
        interval =  time.perf_counter() - start
        print(f'函数的调用执行时间为:{interval}')
        
    return inner


@runtime
def func():
    for i in range(5):
        print(i, end=" ")
        time.sleep(1)

func()

输出:
在这里插入图片描述
分析:
这里用装饰器runtime装饰了函数funcruntime返回inner,当我们调用func()时就相当于调用了inner();此时首先记录当前时间,然后调用func(),最后计算时间差并输出

有参数函数

import time


def runtime(f):
    
    def inner(n):
        start = time.perf_counter()
        f(n)
        interval =  time.perf_counter() - start
        print(f'函数的调用执行时间为:{interval}')
        
    return inner


@runtime
def func(n):
    for i in range(n):
        print(i, end=" ")
        time.sleep(1)

func(9)

输出:
在这里插入图片描述

装饰器嵌套

def outer(f):
    
    def inner():
        print('第二个被执行的部分') # print2
        f()
        print('第三个被执行的部分') # print3
 
    return inner


def extra(f):

    def extra_inner():
        print('第一个被执行的部分') # print1
        f()
        print('最后被执行的部分') # print4
    return extra_inner


@extra
@outer
def func():
    print('函数主体')

func()

输出:
在这里插入图片描述

分析:
先使用离得近的outer装饰器装饰func函数,返回了一个inner函数,再使用上面的extra装饰器,装饰返回的inner函数,并返回extra_inner函数。执行过程:

inner
extra_inner
func()
print2
print3
f()
print1
print4
func()

带有参数的装饰器

如果你的装饰器需要参数,那么就给当前的装饰器套一个壳,用于接收装饰器的参数

def extra(var):

    def outer(func):
    
        def inner1():
            print('功能1')
            func()
            
        def inner2():
            print('功能2')
            func()
        
        if var == 1:
            return inner1
        else:
            return inner2
    return outer


@extra(2)
def func():
    print('函数主体')

func()

输出:
在这里插入图片描述

用类装饰器装饰函数

import time


class RunTime:
    # 魔术方法:当把该类的对象当作函数调用时自动触发
    def __call__(self, func):
        self.func = func  # 把传进来的函数作为对象的成员方法
        return self.inner  # 返回一个函数

    # 在定义的需要返回的新方法中 去进行装饰和处理
    def inner(self, var):
        start = time.time()
        self.func(var)
        print('函数运行时长:', time.time() - start)


@RunTime()
def func(nums):
	# 插入排序
    for i in range(len(nums)):
        for j in range(i):
            if nums[i] < nums[j]:
                nums = nums[:j] + [nums[i]] + nums[j:i] + nums[i + 1:]
    print(nums)


import random

nums = list(range(20))
random.shuffle(nums)
print(nums)
func(nums)
print(func)

输出:
在这里插入图片描述
分析:
用类做装饰器时相当于把类当作函数调用,这会触发__call__魔术方法,返回inner函数;最后一行的输出说明func确实是RunTime类的函数

用类方法装饰函数

import time

class RunTime():

    def outer(func):
        RunTime.func = func  # 把传递进来的函数定义为类方法
        return RunTime.inner # 同时返回一个新的类方法

    def inner(nums):
        start = time.time()
        RunTime.func(nums)
        print('函数运行时长:', time.time() - start)


@RunTime.outer
def func(nums):
    # 插入排序
    for i in range(len(nums)):
        for j in range(i):
            if nums[i] < nums[j]:
                nums = nums[:j] + [nums[i]] + nums[j:i] + nums[i + 1:]
    print(nums)


import random

nums = list(range(20))
random.shuffle(nums)
print(nums)
func(nums)
print(func)

输出结果和上例一样,原理和普通函数装饰器大体相同

装饰器装饰类

用装饰器装饰类,目的是在不改变类的定义和调用的情况下给类增加新的成员(属性或方法)

用函数装饰类

def extra(cls):

    def func2():
        print('我是在装饰器中追加的新方法')
    cls.func2 = func2 # 把刚才定义的方法赋值给类
    cls.attr = '我是在装饰器中追加的新属性'

    #返回时,把追加了新成员和新方法的类返回
    return cls


@extra
class Demo():
    def func():
        print('我是Demo类中定义的方法')

Demo.func() # 此时的Demo类是通过装饰器更新过的Demo类
Demo.func2()
print(Demo.attr)

输出:
在这里插入图片描述

用类装饰类

class Extra():

    def __call__(self, cls):
        # 把接收的类赋值给当前对象作为一个属性
        self.cls = cls
        # 返回一个函数
        return self.newfunc

    def newfunc(self):
        self.cls.attr = '我是在类装饰器中追加的新属性'
        self.cls.func2 = self.func2
        # 返回传递进来的类的实例化结果
        return self.cls()

    def func2(self):
        print('我是在类装饰器中追加的新方法')


@Extra()
class Demo():
    def func(self):
        print('我是Demo类中定义的方法')

obj = Demo()
obj.func()
obj.func2()
print(obj.attr)
print(obj)

输出:
在这里插入图片描述
分析:
用类做装饰器时相当于把类当作函数调用,这会触发__call__魔术方法,返回self.newfunc函数,因而实例化Demo类相当于调用了self.newfunc函数,该函数 给Demo类增加了一个新方法和一个新属性,并将Demo类的实例化结果返回

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值