Python中的装饰器

装饰器 decorator

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

装饰器定义

核心思想:用一个函数(或者类)去装饰一个旧函数(或者类),造出一个新函数(或者新类)

语法规则:在原有的函数上加上 @符,装饰器会把下面的函数当作参数传递到装饰器中,@符又被成为 语法糖

应用场景:引入日志,函数执行时间的统计,执行函数前的准备工作,执行函数后的处理工作,权限校验,缓存等

###1.装饰器原型(闭包)

​ 利用闭包,把函数当作参数传递,并且在函数内去调用传递进来的函数,并返回一个函数。

#定义外函数,接收一个函数作为参数
def outer(f):
    #定义内函数,并且在内函数中调用了外函数的参数
    def inner():
        print('456')
        f()
    return inner #外函数的返回值为内函数,此时内函数就是闭包函数

#定义普通函数
def func():
    print('123')

func() #作为普通函数直接调用
func = outer(func) #outer返回inner函数,并赋值给了func
func()  #此时再调用func函数时,就等于调用了inner函数
>>>123
   456
   123

改为装饰器用法:

#定义外函数,接收一个函数作为参数
def outer(f):
    #定义内函数,并且在内函数中调用了外函数的参数
    def inner():
        print('456')
        f()
    return inner #外函数的返回值为内函数,此时内函数就是闭包函数

@outer  #此处使用的@outer的语法就是把outer作为了装饰器,等同于 func = outer(func)
#此时上面那一大块函数就变成了装饰器!
def func():
    print('123')

func() #func函数经过了outer装饰器进行了装饰,代码和调用方法不变,但是函数的功能发生了改变
>>>456
   123

2.装饰器的应用:统计函数的执行时间

# 装饰器应用场景-统计函数执行时间

import time

# 定义一个统计函数执行时间的 装饰器
def outer(f):
    #定义内函数,并且在内函数中调用了外函数的参数
    def inner():
        start = time.perf_counter()
        f()
        end = time.perf_counter() - start
        print(f'函数调用时间为:{end}')
    return inner #外函数的返回值为内函数,此时内函数就是闭包函数


@outer  #此处使用的@outer的语法就是把outer作为了装饰器,等同于 func = outer(func)
def func():
    for i in range(5):
        print(i,end = ' ')
        time.sleep(1)

func()

>>>0 1 2 3 4 函数调用时间为:5.0028037

3.装饰器嵌套语法

# 1.定义装饰器
# 外函数
def outer(f):
    #内函数
    def inner():
        print('3')
        f()  # 在内函数中调用外函数中的行参-函数
        print('4')
    # 在外函数中返回内函数
    return inner

# 2.再定义一个装饰器
def kuozhan(f):
    def kzinner():
        print('1')
        f()
        print('2')
    return kzinner

# 3. 装饰器的嵌套 先执行下面的,再执行上面的。
@kuozhan # (2.)再使用上面的 kuozhan 装饰器,装饰 上一次返回的 inner 函数,又返回了 kzinner 函数
@outer   # (1.)先使用离得近的 outer装饰器 装饰func函数,返回了一个 inner函数
def func():
    print('5')

func()
>>>1
   3
   5
   4
   2

''' 过程的解析
1 先使用离得近的 outer装饰器 装饰func函数,返回了一个 inner函数
2 再使用上面的 kuozhan 装饰器,装饰 上一次返回的 inner 函数,又返回了 kzinner 函数

最后在调用func函数的时候是怎么执行的:
    func() == kzinner()
                ===>  1
                ===>  inner()  
                           ===> 3
                           ===> func() ===> 5
                           ===> 4
                ===>  2
'''

4.对带有参数的函数进行装饰

#  对带有参数的函数进行装饰

# 定义装饰器
def outer(f):
    # 如果装饰器带有参数的函数,需要在内函数中定义行参,并传递给调用的函数。
    # 因为调用原函数等于调用内函数;
    def inner(n):
        print('调用原函数,最终执行的是内函数')
        f(n)
    return inner

# 有参数的函数
@outer
def func(n):
    print(f'带有参数{n}的函数')

func(1)
>>>调用原函数,最终执行的是内函数
   带有参数1的函数
    
'''
func() ==> inner()  调用func等于调用inner
    func(...) ==> inner(...)  func里有...参数,那么inner里也得有
        inner(...) ==> func(...) inner里的各种参数才能传给内函数中的func,才能实现原函数func的功能
'''

5.对多参数的函数进行装饰

#  对多参数的函数进行装饰

# 定义装饰器
def outer(f):
    # 如果装饰器带有参数的函数,需要在内函数中定义行参,并传递给调用的函数。
    # 因为调用原函数等于调用内函数;
    def inner(a,b,*args,**kwargs):
        print('和装饰带单个参数的函数类似啦')
        f(a,b,*args,**kwargs)
    return inner

@outer
def func(n,m,*args,**kwargs):
    print(f'带有参数{n}和{m}的函数')
    print(f'想吃{args}')
    print(f'这里就是想复习一下{kwargs}')

func(1,'abc','炸鸡','牛蛙',zxc = '函数里的形参们',abc = '今天网实在不太好')
>>>和装饰带单个参数的函数类似啦
   带有参数1和abc的函数
   想吃('炸鸡', '牛蛙')
   这里就是想复习一下{'zxc': '函数里的形参们', 'abc': '今天网实在不太好'}

'''
func() ==> inner()  调用func等于调用inner
    func(...) ==> inner(...)  func里有...参数,那么inner里也得有
        inner(...) ==> func(...) inner里的各种参数才能传给内函数中的func,才能实现原函数func的功能
'''

6.带有参数的装饰器

会遇到带有参数的装饰器,例如Django框架中的 @login_required(login_url=’/accounts/login/’)

# 带有参数的装饰器

# 如果装饰器需要有参数,那么给当前的装饰器套一个壳,用于接收装饰器的参数
def ke(n):
    def outer(i):
        def inner1():
            print('你现在看到的装饰器的参数为0')
            i()
        def inner2():
            print('你现在看到的装饰器的参数为1')
            i()
        # 装饰器壳的参数,用于在外函数内去做流程控制
        if n == 0:
            return inner1
        else:
            return inner2
    return outer

@ke(1)  # ke(var) ==> outer() ==> outer(func) ==> inner()
def func():
    print('带有参数的装饰器需要有个壳')

func()
>>>你现在看到的装饰器的参数为1
   带有参数的装饰器需要有个壳

7.用类装饰器装饰函数

#老师讲的方法
#类装饰器来装饰函数
class Outer():

    # 魔术方法:当把该类的对象当作函数调用时,自动触发
    def __call__(self,funcc):
        self.func = funcc
        return self.inner

    def inner(self,i):
        print('老师讲的方法')
        self.func(i)

@Outer()  #@Outer() ==>  @obj  ==>  obj(func)   ==>    __call__(func) ==> inner()
def func(n):
    print(f'{n}终于快学完了!')

func('cc')
print(func) #此时的 func就是属于Outer类的对象中的inner方法
'''
print(func)的结果为:
<bound method Outer.inner of <__main__.Outer object at 0x000001D672DEFCC8>>
此时的 func就是属于Outer类的对象中的inner方法
'''
#自己一开始琢磨的方法
#类装饰器来装饰函数
class Outer():

    # 魔术方法:当把该类的对象当作函数调用时,自动触发
    def __call__(self,n):
        def inner(a):
            print('把__call__当成外函数')
            n(a)
            print('此时n是原函数的名字,a是原函数的形参')
        return inner


@Outer()
def func(n):
    print(f'{n}终于快学完了!')

func('cc')

'''
Outer() ==> 等于实例化了一个对象obj
所以:@Outer() ==> @obj
@obj ==> obj(func)   把对象当函数调用时会自动触发Outer类里的__call__魔术方法
所以:==> __call__(func) ==> inner()  此时的__call__就是外函数
'''

print(func)
'''
此时print(func)的结果为:
<function Outer.__call__.<locals>.inner at 0x000001F9C2D95B88>
'''

8.用类方法装饰函数

class Outer():

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

    def inner():   #内函数
        print('老师讲的方法')
        Outer.func()

@Outer.outer # Outer.outer(func) ==> Outer.inner
def func():
    print('终于快学完了!')

func()   # func()  ==> Outer.inner()
#自己琢磨的
#不过这样好像就没有用类的必要了,可以直接用函数当装饰器
class Outer():

    def outer(funcc):   #外函数
        def inner():    #内函数
            funcc()
            print('这样也可以')
        return inner

@Outer.outer
def func():
    print('终于快学完了!')

func()

到目前为止以上所以形式的装饰器,包括 函数装饰器,类装饰器,类方法装饰器,都有一个共同特点:都是在给函数去进行装饰,增加功能。还有一种装饰器,是专门装饰类的:

##用装饰器装饰类

在类的定义的前面使用 @装饰器 这种语法
@装饰器
class Demo():
pass

装饰器给函数进行装饰,目的是不改变函数调用和代码的情况下给原函数增加了新的功能。

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

9.用函数装饰器装饰类

# 定义函数,接收一个类。返回修改后的类
def kuozhan(cls):
    def new():
        print('装饰器中追加的方法')
    cls.new = new # 把刚才定义的方法赋值给类
    cls.name = '装饰器中追加的属性'

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

@kuozhan # @kuozhan ==> kuozhan(Demo) ==> cls ==> Demo
class Demo():
    def func():
        print('这是一个绑定类方法')

Demo.func()  # 此时在调用的Demo类是通过装饰器更新过的Demo类
Demo.new()
print(Demo.name)
>>>这是一个绑定类方法
   装饰器中追加的方法
   装饰器中追加的属性

###10.使用类装饰器装饰类

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

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

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



@KuoZhan()  # KuoZhan() ==> obj ==> @obj(Demo) ==> __call__(Demo) ==> newfunc
class Demo():
    def func(self):
        print('我是Demo类中定义的func方法')

obj = Demo()  # @KuoZhan() ==> @obj ==> obj(Demo) ==> __call__(Demo) ==> newfunc
obj.func()
obj.func2()
print(obj.name)
>>>我是Demo类中定义的func方法
   我是在类装饰器中追加的新方法 func2
   我是在类装饰器中追加的新属性 name

# 此时的obj依然是Demo类的实例化对象,只不过经过装饰后,增加了新的属性和方法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值