面向对象--装饰器(定义、应用场景、语法:装饰器的嵌套、装饰带有参数(多参数)的函数、类装饰器装饰函数等)

75 篇文章 2 订阅
66 篇文章 5 订阅
一、装饰器定义
装饰器 decorator
**在不改变原有函数代码,且保持原函数调用方法不变的情况下,给原函数增加新的功能(或者给类增加属性和方法)**
**核心思想**:用一个函数(或者类)去装饰一个旧函数(或者类),造出一个新函数(或者新类)
**应用场景**:引入日志,函数执行时间的统计,执行函数前的准备工作,执行函数后的处理工作,权限校验,缓存等
**语法规则**:在原有的函数上加上 @符,装饰器会把下面的函数当作参数传递到装饰器中,@符又被成为 语法糖

# 1。 装饰器的原型

### 利用闭包,把函数当作参数传递,并且在函数内去调用传递进来的函数,并返回一个函数
# 定义外函数,接收一个函数作为参数
def outer(f):
    # 定义内函数,并且在内函数中调用了外函数的参数
    def inner():
        print('我是外函数中的内函数1')
        f()
        print('我是外函数中的内函数2')
    return inner


# 定义普通函数
def old():
    print('我是一个普通的函数')

# old()  # 作为普通函数直接调用
old = outer(old)  # outer返回了inner函数,赋值给了old
old()             # 此时再调用old函数时,等同于调用了 inner 函数

'''
**将上面定义的普通函数改为下列装饰器的操作**
'''

# 改为装饰器用法后,就可以直接调用old(),不需要还去给old赋值了
@outer      # 此处使用的@outer的语法就是把outer作为了装饰器,等同于 old = outer(old)
def old():
    print('我是一个普通的函数')

old()  # old函数经过 outer装饰器进行了装饰,代码和调用方法不变,但是函数的功能发送了改变---------直接调用,简单易操作

二、装饰器应用场景-统计函数执行时间
import time

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

@runtime
def func(): # 定义一个函数
    for i in range(5):
        print(i,end=' ')
        time.sleep(0.5)
func() # 输出结果为:0 1 2 3 4 函数调用执行的时间为:2.5545405

三、装饰器的嵌套
# 定义装饰器
# 外函数
def outer(func):
    def inner(): #内函数
        print('昨天长沙有点冷,好在没下雨 3')
        func() # 在内函数中调用外函数中的行参-函数
        print('看看天气预报明天的天气怎么样 4')
    return inner  # 我经常会忘记返回一个值,注意注意# 在外函数中返回内函数

def extend(f):
    def miniinner():
        print('我是一个扩展的函数1')
        f()
        print('我也是一个扩展函数 2')
    return miniinner # 在外函数中返回内函数

          #  装饰器的嵌套 先执行下面的,再执行上面的。
@extend   # 2。再使用上面的 extend 装饰器,装饰 上一次返回的 inner 函数,又返回了 miniinner 函数
@outer   # 1。先调用离得近的 outer装饰器 装饰love函数,返回了一个 inner函数
def love():
    print('今天是个阳光明媚的日子 5')

love()

'''
运行的顺序:1.outer装饰器 装饰love函数,返回了一个 inner函数
            2.将inner函数作为参数再次利用extend()进行调用,返回了一个 miniinner函数
     1      3.输出 (1).print('我是一个扩展的函数1')

     3             (2).f()------这个其实就是inner()------innner()会输出:print('昨天长沙有点冷,好在没下雨 3')
     5                                                                    func()  # 在内函数中调用外函数中的行参-函数----func()输出:今天是个阳光明媚的日子 5
     4                                                                    print('看看天气预报明天的天气怎么样 4')

     2             (3).print('我也是一个扩展函数 2')

'''
四、装饰带有参数的函数
def outer(func):
    # 如果装饰带有参数的函数,需要在内函数中定义行参,并传递给调用的函数。因为调用原函数等于调用内函数
    def inner(var): # 传行参
        print('昨天长沙有点冷,好在没下雨')
        func(var) # 传行参
        print('看看天气预报明天的天气怎么样')
    return inner  # 我经常会忘记返回一个值,注意注意

@outer
def love(adj):   
    print(f'今天{adj}是个阳光明媚的日子')

love('确实')  #调用love() 等于调用 inner() ===>  love('确实') 等于 inner('确实')
五、装饰带有多参数的函数
def outer(func):

    def inner(who,adj,*args,**kwargs): # 不要怕,多少参数原封不动的传进来
        print('昨天长沙有点冷,好在没下雨')
        func(who,adj,*args,**kwargs) # 原样传进来
        print('看看天气预报明天的天气怎么样')
    return inner

@outer
def love(who,adj,*args,**kwargs):
    print(f'{who}说今天{adj}是个阳光明媚的日子')
    print('那我们去逛街买点东西',args)
    print('看个电影',kwargs)

love('Amanda','确实','水乳套装','精华','面膜','眼霜',mov = '八佰')  #调用love() 等于调用 inner() ===>  love('确实') 等于 inner('确实')
六、带有参数的装饰器
# 带有参数的装饰器
# 如果你的装饰器需要有参数,那么给当前的装饰器套一个壳,用于接收装饰器的参数
def extend(var):
    def outer(func):  # 每一层都需要返回一个值
        def inner1():  # 每一层都需要返回一个值
            print('今天天气很好')
            func()
        def inner2():
            print('今天没空')
            func()
        if var == 1:  # 装饰器壳的参数,可以用于在函数内去做流程控制
            return inner1
        else:
            return inner2
    return outer
@extend(2)
def love():
    print('谈谈人生')

love()
七、类装饰器装饰函数
# 类装饰器装饰函数

class Outer():
    # 魔术方法:当把该类的对象当作函数调用时,自动触发 obj()
    def __call__(self,func):
        self.func = func   # 把传进来的函数作为对象的成员方法
        return self.inner    # 返回一个函数(有self的时候返回函数的时候也要加上self)
    def inner(self,who):
        print('又成功学到了一个知识点')
        self.func(who)
        print('吃份火锅犒劳一下自己')

          # 用实例化的对象加上@就等于是将这个对象当作函数去使用,这样就会触发__call__
@Outer() # ---> obj ---> @obj == obj() ---> __call__(love) ---> 返回inner ,调用 inner() ===>  love(who) 等于 inner(who)
def love(who):
    print(f'{who}和你一起谈谈人生和理想')

love('我')
print(love) # 此时的 love就是属于Outer类这个对象中的inner方法

#**结果输出:**
#又成功学到了一个知识点
#我和你一起谈谈人生和理想
#吃份火锅犒劳一下自己
#<bound method Outer.inner of <__main__.Outer object at 0x0000028CA33E8460>>

八、用类方法装饰函数
# 用类方法装饰函数(类里面的一个方法)
class Outer():
    def newinner(func):
        Outer.func = func   # 把传递进来的函数定义为类方法
        return Outer.inner  # 同时返回一个新的类方法
    def inner():
        print('今天天气很好')
        Outer.func()
        print('但是没有时间呀')

@Outer.newinner  # Outer.newinner(love) ==> Outer.inner
def love():
    print('一起谈谈人生,聊聊理想')

love()    # love()  ==> Outer.inner()

# 到目前为止以上所以形式的装饰器,包括 函数装饰器,类装饰器,类方法装饰器,都有一个共同特点:都是在给函数去进行装饰,增加功能。
装饰器给函数进行装饰,
目的是不改变函数调用和代码的情况下给原函数增加了新的功能。

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

# 定义函数,接收一个类。返回修改后的类

def extend(cls):
    def func2():
        print('我是在装饰器中追加的新方法,func2')
    cls.func2 = func2 # 把刚才定义的方法赋值给 类
    cls.name = '我是在装饰器中追加的新属性 name'
    return  cls #返回时,把追加类新成员的 类 返回去

@extend # extend(Demo) ==> cls ==> Demo
class Demo():
    def func():
        print('我是Demo类中定义的func方法')

Demo.func() # 此时在调用的Demo类是通过装饰器,更新过的Demo类
Demo.func2()
print(Demo.name)

十、使用类装饰器装饰类
# 使用类装饰器装饰类

class Extend():
    def __call__(self, cls):
        self.cls = cls  # 把接收的类,赋值给当前对象,作为一个属性
        return self.newfunc  # 返回一个函数
    def func2(self):
        print('我是在类装饰器中追加的新方法 func2')
    def newfunc(self):
        self.cls.name = '我是在类装饰器中追加的新属性 name'
        self.cls.func2 = self.func2
        return self.cls()  # 返回传递进来的类的实例化结果,obj

@Extend()  #  obj ==> @obj(Demo) ==> __call__(Demo) ==> newfunc
class Demo():  
    def func(self):
        print('我是Demo类中定义的func方法')
obj  = Demo()   # Demo() ==> newfunc() ==> obj
obj.func()
obj.func2()
print(obj) # 此时的obj依然是Demo类的实例化对象,只不过经过装饰后,增加了新的属性和方法

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值