Python装饰器

本文详细介绍了装饰器的概念,包括闭包的作用与形成条件,以及如何通过闭包实现装饰器。探讨了装饰器如何遵循开放封闭原则,提供带返回值和参数的装饰器实例,并展示了标准装饰器的使用和多装饰器的组合。最后给出了一个通用的装饰器模板和递归函数的相关内容。
摘要由CSDN通过智能技术生成

前言

此刻并没有偷懒,哈哈哈。这块便是一个重头戏–装饰器了,在装饰器之前,会先对闭包有一个总的介绍,这样会理解装饰器更加容易些,话不多说,直接share吧。

闭包

  • 为了保障数据的安全,使用闭包
  • 首先,我们以完成一个计算不断增加的系列值的平均值的需求为例子引出闭包,如下:
 可变数据类型
lis = []
def make_averager(new_value):
    lis.append(new_value)
    total = sum(lis)
    return total / len(lis)
print(make_averager(100000))
'''
若在此对lis进行相关操作,后续结果错误,全局变量lis无法保证数据的安全
lis.append(888)
'''
print(make_averager(110000))
print(make_averager(120000))
#若放在函数中,每次调用函数会销毁空间,开辟新空间,无法在上次数据的基础上进行操作
#闭包思路:
def make_averager():
    series = []
    def averager(new_value):
        series.append(new_value)
        total = sum(series)
        return total / len(series)
    return averager
avg=make_averager()
print(avg(100000))
print(avg(110000))
print(avg(120000))
'''
100000.0
105000.0
110000.0
'''

#不可变数据类型
def sum():
    count = 1
    def summ(i):
        nonlocal count
        count += i
        return count
    return summ
x = sum()
print(x(2)) #3
print(x(3)) #6
  • 如上可以得到闭包的形成条件:

    • 闭包存在于嵌套函数中

    • 内层函数对外层函数非全局变量引用(改变)

    • (参考:函数名逐层返回直至返回到最外层)
      在这里插入图片描述

  • 闭包函数的空间不会随着函数的结束而消失(被引用的变量–>(自由变量)不会消失)

  • 判断一个函数是否闭包 == 闭包函数有没有自由变量

  • print(函数名._code_.co.freevars)

    • eg:
    def wrapper(a, b):
        def inner():
            print(a)
            print(b)
         return inner
    a = 2
    b = 3
    ret = wrapper(2, 3)
    #闭包
    print(ret.__code__.co_freevars)  #('a', 'b')
    
  • 闭包的应用

    • 保证数据的安全
    • 装饰器的本质
  • 即:闭包是产生在嵌套函数中,内层函数对外层函数非全局变量进行引用,并且自由变量不会随着函数的结束而消失

装饰器

开放封闭原则

  • 开放原则:更新一些额外的功能(对扩展开放)
  • 封闭原则:不要改变源码(对修改封闭)
  • python装饰器:完美的诠释了开放封闭原则
  • 装饰器就是一个函数:他要装饰一个函数,在不改变原函数的源码以及调用方式的前提下,给其增加一个额外的功能

装饰器初识

  • 过程

  • #写一个函数,测一个函数执行速率
    #1
    def index():
        time.sleep(3)
        print('话u你要')
    import time
    start_time = time.time()
    index()
    end_time = time.time()
    print(f"此函数执行效率{end_time - start_time}")
    #2需测试多个函数执行效率 写函数,否则重复代码太多
    def func1():
        time.sleep(1)
        print('welcome')
    def func2():
        time.sleep(2)
        print('bye')
    def test_time(x):
        start_time = time.time()
        x()
        end_time = time.time()
        print(f"此函数的执行效率{end_time - start_time}")
    test_time(func1)
    test_time(func2)
    #开放原则满足,封闭原则:不改变函数的源码,以及调用方式
    #违反了封闭原则:改变了函数的调用方式
    #3当函数调用时就自带测试效率,且满足封闭开放原则
    def test_time(x):
        def inner():
            start_time = time.time()
            x()
            end_time = time.time()
            print(f'此函数的执行效率{end_time- start_time}')
         return inner
    @test_time #index = test_time(index) 写于装饰函数后面 等价于 @test_time写于装饰函数前
    def index():
        time.sleep(2)
        print('欢迎访问')
    index()
    

在这块重要的点是 @test_time写于装饰函数前等价于test_time(index)写于装饰函数后面,这样有利用理解装饰器的调用过程

带返回值的装饰器

  • 引用上面装饰器函数的例子,当给index()加上返回值时,print(index())的返回值与应返回值不符,违背开放封闭原则

  • def test_time(x):
        def inner():
            start_time = time.time()
            x()
            end_time = time.time()
            print(f'此函数的执行效率{end_time- start_time}')
        return inner
    @test_time 
    def index():
        time.sleep(2)
        print('欢迎访问')
        return True
    index()
    print(index()) #None  inner()
    
  • 修改如下,使带返回值的被装饰函数满足开放封闭原则

  • def test_time(x):
        def inner():
            start_time = time.time()
            x()
            ret = x()
            end_time = time.time()
            print(f'此函数的执行效率{end_time- start_time}')
            return ret
        return inner
    @test_time 
    def index():
        time.sleep(2)
        print('欢迎访问')
        return True
    index()
    print(index()) #True
    

标准的装饰器

  • 继续以上述为例

  • 当给index()函数加上参数n时,无论加不加装饰器,你的实参应该传给形参n,但index(实参)调用的仍然是一个没有形参的inner()函数,便会报错 index(‘ly’) == inner(‘ly’)

  • def test_time(x):
        def inner():
            start_time = time.time()
            ret = x()
            end_time = time.time()
            print(f'此函数的执行效率{end_time- start_time}')
        return ret
    return inner
    @test_time
    def index(n):
        time.sleep(2)
        print(f'欢迎{n}访问')
        return True
    index() #index() missing 1 required positional argument: 'n'
    
    
  • 进行修改

  • def test_time(x):
        def inner(n1):
            start_time = time.time()
            ret = x(n1)
            end_time = time.time()
            print(f'此函数的执行效率{end_time- start_time}')
        return ret
    return inner
    @test_time
    def index(n): 
        time.sleep(2)
        print(f'欢迎{n}访问')
        return True
    index('ly')
    '''
    欢迎ly访问
    此函数的执行效率2.0023210048675537
    '''
    
    
  • 但此装饰器只满足了一个参数的需求,当参数不定长时,需使用万能参数

  • import time
    def time1(n):
        def inner(*args, **kwargs):
            //函数的定义:* ** 聚合
            //args = (1, 2)
            start_time = time.time()
            ret = n(*args, **kwargs)
            //函数的执行:* ** 打散
            //n(*(1, 2)) == n(1, 2) 
            end_time = time.time()
            print(f'此函数执行效率{start_time - end_time}')
        return ret
    return inner
    @time1 #index1 = time(index1)
    def index1(x, y):
        time.sleep(0.5)
        print(f'最终结果:{x + y}')
        return x + y
    index1(1, 2)
    '''
    最终结果:3
    此函数执行效率-0.5012929439544678
    '''
    
    
  • 标准装饰器

  • def warpper(f):
        def inner(*args, **kwargs):
            '''被装饰函数之前的操作'''
            print(666)
            ret = f(*args, **kwargs)
            '''被装饰器函数之后的操作'''
            print("执行完毕")
        return ret 
    return inner
    
    #使用实例
    @warpper
    def func():
        print("你好")
    func()
    '''
    666
    你好
    执行完毕
    '''
    
    

装饰器的应用

  • 在不改变原函数的源码以及调用方式前提下,为其增加额外功能
  • 登陆认证、打印日志等

带参数的装饰器

def wrapper_out(n):
    def wrapper(f):
        def inner(*args, **kwargs):
            username = input("请输入用户名:")
            password = input("请输入密码:")
            with open(n, encoding='utf-8') as f1:
                for line in f1: 
                    user, pwd = line.strip().split('|')
                    if username == user and password == pwd:
                        print("登陆成功")
                        ret = f(*args, **kwargs)
                        return ret
                 return False
          return inner
     return wrapper
@wrapper_out('qq')
def qq():
    print("欢迎访问qq")
@wrapper_out('douyin')
def douyin():
    print("欢迎访问抖音")
qq()
douyin()
带参数的装饰器分两步执行:
'''
@wrapper_out('腾讯')
	1、执行wrapper_out('腾讯')这个函数,把相应的参数'腾讯'传给n,并且得到返回值wrapper函数名     2、将@与wrapper结合,得到我们之间熟悉的标准版的装饰器,然后按照装饰器的执行流程执行
'''
#开发思路:增加耦合性

多个装饰器装饰一个函数

  • 从上往下(执行before,被装饰函数之前的操作),被装饰函数,再从下往上(执行after,被装饰函数之后的操作)
def wrapper1(func1):   #fun1 = f原函数
    def inner1():
        print('wrapper1,before func') #2
        func1()
        print('wrapper1,after func') #4
    return inner1
def wrapper2(func2):   #func2 = inner1
    def inner2():
        print('wrapper2,before func') #1
        func2() 
        print('wrapper2,after func') #5
    return inner2
@wrapper2 #第二步 f = wrapper2(f) 里面的f == inner1外面的 f == inner2
@wrapper1 #离得近,第一步 f = wrapper1(f)里面的f(原函数) == func1 外面的 f == inner1
def f():
    print('in f') #3
f() #inner2()
'''
wrapper2,before func
wrapper1,before func
in f
wrapper1,after func
wrapper2,after func
'''

万能模板

  • 每当使用装饰器时,无论诉求如何,脑子里肯定要先出来一个基本框架的,所以i我愿叫他为万能模板,专门开个小标题给这个神奇的东西
 def warpper(f):
      def inner(*args, **kwargs):
          '''被装饰函数之前的操作'''
          print(666)
          ret = f(*args, **kwargs)
          '''被装饰器函数之后的操作'''
          print("执行完毕")
      return ret 
  return inner

递归函数

  • 自己调用自己

  • 官网规定:默认递归的最大深度1000次

  • 限制递归次数

  • import sys
    sys.setrecursionlimit(10000)
    def func(n):
        print(n)
        n += 1
        func(n)
    func(1)
    
  • 举例

  • '''
    1 ly 18 当在此,结束
    2 xz 18+2
    3 lk 18+2+2
    '''
    def age(n):
        if n == 1:
            return 18
        else:
            return age(n - 1) + 2
    print(age(3))
    #使嵌套列表通过递归,单个元素输出
    lis =[1, 2, [3, 2, [1, [4, 0]]]]
    def prli(li):
        for i in li:
            if type(i) == list:
                prli(i)
            else: 
                print(i)
    prli(lis)
    

ok,那函数这一块就分享结束了,希望我努力的学,后面的是日更还是全部学完一个大的板块进行汇总呢,待我视情况而定,再好好斟酌斟酌。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Acco_30_L

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值