Python Note -- Day 15. 面向对象 - 装饰器

20 装饰器 decorator

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

20.1 装饰器的定义和原型

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

20.1.1 装饰器的原型

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

# 定义外函数,接收一个函数作为参数
def outer(f):
    # 定义内涵数,且在内函数中调用了外函数的参数
    def inner():
        print('this is inner1 from outer')
        f()
        print('this is inner2 from outer')
    return inner

# # 普通函数
# def old():
#     print('this is normal function')
# 
# # old() # 作为普通函数调用
# old = outer(old)  # outer返回了inner函数,赋值给了old
# old()   # 此时调用old函数,等同于调用inner函数

# 改为装饰器
@outer   # 此处使用的语法就是把outer作为装饰器  等同于 old = outer(old)
def old():
    print('this is normal function')

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

20.1.2 装饰器的应用场景

统计函数执行时间

import time

# 定义一个统计函数执行时间的装饰器
def runtime(f):
    def inner():
        start = time.perf_counter()
        f()
        end = time.perf_counter() - start
        print(f'the run time is {end}')
    return inner

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

func()

20.2 装饰器的语法

20.2.1 装饰器的嵌套

1.普通装饰器的定义

# 定义装饰器
# 外函数
def outer(f):
    # 内涵数
    def inner():
        print('find a girl')
        # 内函数中调用外函数的形参-函数
        f()
        print('say goodbye')
    # 外函数中返回内涵数
    return inner

@outer
def love():
    print('make a conversation')

love()

2.两个装饰器

# 外函数
def outer(f):
    # 内涵数
    def inner():
        print('find a girl 3')
        # 内函数中调用外函数的形参-函数
        f()
        print('say goodbye 4')
    # 外函数中返回内涵数
    return inner

def extent(f):
    def inner():
        print('extention 1')
        f()
        print('extention 2')
    return inner

@extent
@outer
def love():
    print('make a conversation 5')

love()

'''
1. 先使用离得近的outer装饰器,装饰love函数,返回一个inner函数
2. 再使用extent 装饰器,装饰上面返回的inner,又返回extent函数

调用love函数的顺序
love() == extent()
         == 1
         == inner() 
              == 3
              ==love()==5
              == 4
         == 2
装饰器的嵌套,先执行下面的,在执行上面的
@extent
@outer
'''

20.2.2 装饰带有参数的函数

装饰器带有形参,需要在内函数中定义形参,并传递给调用的形参
因为调用原函数等于调用内涵数

def outer(f):
    def inner(n):
        print(f'find a girl {n}')
        f(n)
        print('say goodbye')
    return inner

@outer
def love(name):
    print(f'make a conversation with {name}')

love('Oli')

20.2.3 装饰带有多参数的函数

def outer(f):
    def inner(n,m,*args,**kwargs):
        print(f'find a girl {n}')
        f(n,m,*args,**kwargs)
        print('say goodbye')
    return inner

import time
@outer
def love(name,na,*args,**kwargs):
    print(f'make a conversation with {name} and {na}')
    print('went to next around',args)
    print('also went to watch file',kwargs)

love('Oli','Mark','dance','KTV','drink',mov='insidious')

20.2.4 带有参数的装饰器

若装饰器需要有参数,那么给当前的装饰器套一个壳,用于接收装饰器的参数

  • 装饰器壳的参数,可以用于在函数内去做流程控制
def ke(var):
    def outer(f):
        def inner1():
            print('find a girl')
            f()
        def inner2():
            print('find out its boy')
            f()
        if var =='rich':
            return inner1
        else:
            return inner2
    return outer

@ke('rich')  # ke(var) == outer() == outer(love) == inner()
def love():
    print('make a conversation')

love()

Django 身份验证系统 @login_required(login_url='/acounts/login/')

20.2.5 用类装饰器装饰函数

class Outer():

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

    # 在定义的需要返回的新方法中,进行装饰和处理
    def inner(self,n):
        print('1')
        self.f(n)
        print('2')

@Outer()  # outer() == obj  @obj == obj(love) == __call__(love) == inner()
def love(name):
    print(f'make a conversation with {name}')

love('aa')
print(love) # 此时 love 属于 Outer类这个对象中的inner方法
# <bound method outer.inner of <__main__.outer object at 0x0000000001DEBE88>>

20.2.6 用类方法装饰函数

class Outer():

    def inner(f):
        Outer.f = f    # 把传递进来的函数定义为类方法
        return Outer.innn  # 同时返回一个新的类方法,(形成闭包)

    def innn(n):
        print('1')
        Outer.f(n)
        print('2')

@Outer.inner    # Outer.inner(love) == Outer.innn  
def love(name):
    print(f'make a conversation with {name}')

love('aa')
print(love)  # <function Outer.inn at 0x0000000001E6A3A8>

Django web的认证系统 @permission_required('polls.can_vote',login_url='loginpage/')

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

还有一种装饰器,专门装饰类,就是在类的定义的前面,使用@装饰器 这种语法

20.2.7 使用函数 装饰器装饰类

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

# 定义函数,接收一个类,返回修改后的类
def extent(cls):
    def func1():
        print('this is new method from decorator')
    cls.func1 = func1   # 把刚才定义的方法赋值给类
    cls.ff = 'this is new function from decorator'

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

@extent
class Demo():
    def func(self):
        print('this is func from class demo')

Demo.func(1) # 此时再调用的demo类是通过装饰器,更新过的Demo类
# 通过装饰器新增的成员
Demo.func1()
print(Demo.ff)

20.2.8 使用类 装饰器装饰类

class extent():

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

    def newfunc(self):
        self.cls.ff = 'this is new function from decorator'
        self.cls.func2 = self.func2
        # 在方法中 返回传递进来的类的实例化结果,obj
        return self.cls()

    def func2(self):
        print('this is new method from decorator')


@extent()   # extent() == obj == @obj(Demo) == __call__() == newfunc()
class Demo():
    def func(self):
        print('this is func from class demo')

obj = Demo() # Demo() == func() == obj
obj.func()
obj.func2()
print(obj.ff)
# 思考,此时的obj 是哪个类的对象
print(obj)

思考,如何用装饰器装饰带有返回值得函数 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值