Pythons新手村Boss装饰器

python新手村Boss装饰器

  • 装饰器简介

  • 无产装饰器

  • 有参装饰器

  • 装饰器模版

  • 装饰器语法糖及修复技术

  • 多层装饰器

  • 装饰器简介

  1. 概念:
    在不改变被装饰器源代码和调用方式的情况下给被装饰的对象添加新的功能
  2. 装饰器的本质:
    装饰器并不是一个新的技术,而是由函数参数,名称空间,函数名多种用法,闭包函数所组成到一起的结果
  3. 口诀:
    对修改封闭,对扩展开放
  • 储备知识

这里需要先需要提前使用一个time包来实现一下时间戳功能,代码如下

  • 时间相关操作(time)
import time  # time 获取时间戳
print(time.time())  # 时间戳(距离1970-01-01 00:00:00所经历的秒数)
time.sleep(3)  # 程序暂停3秒再运行
print('这里是代码体')
print(time.time())  # 代码运行结束的时间戳

1665485696.789707
这里是代码体
1665485699.794832


2. 小程序,获取当前代码运行的时长
import time

count = 0

start_time = time.time()
while count < 10000000:
    print('运行中...')
    count += 1
end_time = time.time()
print(f'当前程序运行需要的时长为{end_time - start_time}')

当前程序运行需要的时长为16.997233867645264
# 这里是time的用法
  • 装饰器的推导流程(重要)
*1*. 第一部分
import time

def index():
    time.sleep(3)
    print('from index')

def home():
    time.sleep(1)
    print('from home')

#  直接在调用index函数的前后添加代码
#start_time = time.time()
#index()
#end_time = time.time()
#print('函数index的执行时间为: ', end_time - start_time)


*2*. index调用的部分不较多,代码不可能反复拷贝>>>相同的代码需要在不同的位置反复执行函数
#def get_time():
#    start_time = time.time()
#    index()
#    end_time = time.time()
#    print('函数index的执行时间为>>>: ', end_time -  start_time)

#get_time()
from index
函数index的执行时间为>>>:  3.0051331520080566


*3*. 函数体代码写死了,只能统计index的执行时间,如何才能做到统计更多的函数运行时间,直接传参变换统计的函数
def get_time(xxx):
    start_time = time.time()
    xxx()
    end_time = time.time()
    print('当前函数的执行时间为>>>', end_time - start_time )

get_time(index)
get_time(home)

from index
当前函数的执行时间为>>> 3.004418134689331
from home
当前函数的执行时间为>>> 1.0000019073486328


*4*. 虽然实现了一定的兼容性,但是并不符合装饰器的特征,第一种传参不写只能考虑闭包
1. def outer(xxx):
   5.def get_time():
        start_time = time.time()
    6.  xxx()
        end_time = time.time()
        print('函数的执行时间为:', end_time - start_time)
  3.  return get_time
2. res = outer(index)
4. res()
8. res1 = outer(home)
9. res1()
from index
函数的执行时间为: 3.0043351650238037
from home
函数的执行时间为: 1.0040619373321533
"""
推导解析:
1. 定义函数outer函数并需要1个形参
2. 先调用outer函数将index函数名以位参的形式传入
3. 将闭函数名get_time赋值为2号代码的res
4. res()调用就是get_time()执行
5. 执行get_time代码,先获取当前时间戳
6. 当前xxx值是1号代码传入的index,index执行结束后获取当前时间戳
7. 此时index的运行时长就获取到了
8. 8号和9号的代码同样重复1~6的过程

"""

*5*.调用方式还是不对 如何变形> 变量名赋值绑定(******)
1. def outer(xxx):
    4. def get_time():
           start_time = time.time()
        5. xxx()
           end_time = time.time()
        6. print('函数执行的时长为: ', end_time - start_time)
    2.1return get_time

2. index = outer(index)
3. index()
8. home = outer(home)
9. home()

from index
函数执行的时长为:  3.005247116088867
from home
函数执行的时长为:  1.0051252841949463

"""
推导解析:
1. 先定义一个具有形参的outer函数
2. 掉用outer函数并将index的函数名传入,
2.1 并返回闭包的函数名,赋值给index,注意此时index的绑定关系是get_time函数名
3. 调用index()函数,相当于调用get_time函数
4. 运行get_time函数,获取当前时间戳
5. xxx调用outer传入的index原函数名,执行index函数,并获取结束时间戳
6. 打印index函数运行时间
7. 以上推导结束,并实现了无参装饰器
8. 代码8和代码9 推导流程如1~6
"""


**6.** 上述装饰器只能装饰无参函数,兼容性太差
import time
def func(a):
    time.sleep(0.1)
    print('from func', a)

def func1(a, b):
    time.sleep(0.2)
    print('from func1', a, b)

def func2():
    time.sleep(0.3)

def outer(xxx):
    def get_time(a, b):
        start_time = time.time()
        xxx(a, b)
        end_tima = time.time()
        print('函数的执行时间为: ', end_tima - start_time)
    return get_time

func1 = outer(func1)
func1(1, 2)
# func = outer(func)
# func(1)
# func2 = outer(func2)
# func2()

"""
推导解析:
和第五推导过程一致,但是我们这里会发现,这里的装饰器在执行过程中把参数的数量写死了,接下来第7步通过可变长参数修改此问题
"""

**7.** 被装饰的函数不知道有没有参数以及有几个参数 如何兼容
1. def func(a):   
    time.sleep(0.1)
    print('from func', a)

2. def func1(a, b):  
    time.sleep(0.2)
    print('from func1', a, b)

3. def outer(xxx):  
   7.  def get_time(*args, **kwargs): 
          start_time = time.time()
      8.  xxx(*args, **kwargs)
          end_time = time.time()
          print('函数的执行时间为: ', end_time - start_time)
  5.   return get_time
4. func = outer(func)  
6. func(123)
9. func1 = outer(func1)
10.func1(1, 2)


"""
推导解析
1. 定义func
2. 定义func1
3. 定义outer
4. 调用outer并将func函数名传入xxx
5. outer返回值为get_time函数名,赋值给第4步的func
6. 执行func传入123 ,相当于执行get_time并传入123
7. 执行get_time 参数为args(123),获取当前执行时间
8. xxx为第4步func函数的名字,这一步带着123执行func函数,执行完成后获取结束时间并打印
9. 9. 10. 推导流程如1~8
10. 到此完成了一个无视参数的装饰器


"""


**8.** 如果被装饰的函数有返回值
1. def func(a):
       time.sleep(0.1)
       print('from func', a)
       return 'func'
2. def func1(a, b):
       time.sleep(0.2)
       print('from func1', a, b)
       return 'func1'
3.  def outer(xxx):
   6.  def get_time(*args, **kwargs):
           start_time = time.time()
           res = xxx(*args, **kwargs)
           end_time = time.time()
           print('函数的执行时间为: ', end_time - start_time)
           return res
       return get_time

4. func = outer(func)
5. res = func(123)
7. print(res)

func1 = outer(func1)
res = func1(123, 123)
print(res)

from func 123
函数的执行时间为:  0.10509371757507324
func
from func1 123 123
函数的执行时间为:  0.20029377937316895
func1

"""
推导解析:
1. 定义func
2. 定义func1
3. 定义outer
4. 执行outer 并将func函数名传入xxx,返回值get_time给变量func
5. 执行func并传值123,相当于get_time传值123
6. 执行get_time ,获取当前时间,xxx参数调用outer传入的func函数名,执行函数,需要参数获取args参数值为123,执行完毕后返回值赋予res,获取运行完成好的时间并打印
7. 找到函数体内的res,当前返回值为'func'字符串
"""

  • 装饰器模版
# 务必掌握 
def outer(func):
    def inner(*args, **kwargs):
        # 执行被装饰对象之前可以做的额外操作
        res = func(*args, **kwargs)
        # 执行被装饰对象之后可以做的额外操作
        return res
    return inner
  • 语法糖

语法糖需要注意:

  1. 装饰器需要卸载被装饰函数上面
  2. @装饰器名字就相当于原本写的func = outer(func)
def outer(xxx):
    def get_time(*args, **kwargs):
        start_time = time.time()
        res = xxx(*args, **kwargs)
        end_time = time.time()
        print('函数的执行时间为: ', end_time - start_time)
        return res
    return get_time


@outer  # 代替了原本需要写的func = outer(func)
def func(a):
    time.sleep(0.1)
    print('from func', a)
    return 'func'

func(11)

  • 多层语法糖
  1. 多层语法糖由下向上执行
  2. 当执行结束后发现上面还有语法糖会将得到的返回值返回给上面的语法糖
  3. 如果上面没有语法糖了,则结果是index = outter1(wrapper2)
def outter1(func1):
    print('加载了outter1')
    def wrapper1(*args, **kwargs):
        print('执行了wrapper1')
        res1 = func1(*args, **kwargs)
        return res1
    return wrapper1

def outter2(func2):
    print('加载了outter2')
    def wrapper2(*args, **kwargs):
        print('执行了wrapper2')
        res2 = func2(*args, **kwargs)
        return res2
    return wrapper2

def outter3(func3):
    print('加载了outter3')
    def wrapper3(*args, **kwargs):
        print('执行了wrapper3')
        res3 = func3(*args, **kwargs)
        return res3
    return wrapper3


@outter1
@outter2
@outter3  
def index():
    print('from index')
index() 

# 执行结果:
加载了outter3
加载了outter2
加载了outter1
执行了wrapper1
执行了wrapper2
执行了wrapper3
from index

"""
推导解析:
1. outter3将被封装函数index加载,outter3(index)
2. 代码执行outter3,并传入index给func3
3. 打印加载了outter3
4. 由于语法糖上还有语法糖所以return wrapper3 返回值给到outter2
5. outter2(wrapper3), 并执行outter2,传入wrapper3给func2
6. 打印加载了outter2,的到返回值wrapper2,
7. outter2 上还有返回值,outter1(wrapper2),
8. 执行outter1,将wrapper2赋值func1
9. 打印加载了outter1
10. 得到返回值wrapper1,此时语法糖上面以及没有了
11. 变种为index = outter1(wrapper1)
12. 执行wrapper1,打印执行了wrapper1,执行func1,也就是wrapper2,打印执行了wrapper2,func2也就是wrapper3,打印执行了wrapper3
13. 最后执行index() 也就是打印from index


"""

  • 有参装饰器
  1. 当装饰器中需要额外的参数就需要使用有参装饰器
  2. 也就是在装饰器外面再包一层可以传参的函数
def outer_plus(mod):
    def outer(func_name):
        def inner(*args, **kwargs):
            if mod == '1':
                print('数据写死')
            elif mod == '2':
                print('mysql数据')
            elif mod == '3':
                print('文件数据')
            else:
                print('数据来源错误')
            res = func_name(*args, **kwargs)
            return res
        return inner
    return outer

@outer_plus('1')
def func1():
    print('form fun1')
func1()

# 这就是有参装饰器
  • 无参装饰器模版和有参装饰器模版
# 无参装饰器
def outer(func_name):
    def inner(*args, **kwargs):
        res = func_name(*args, **kwargs)
        return res
    return inner

# 有参装饰器模版
def outers(mod):
    def outer(func_name):
        def inner(*args, **kwargs):
            res = func_name(*args, **kwargs)
            return res
        return inner
    return outer
  • 装饰器修复
  1. from functools import wraps 装饰器的伪装
  2. 在闭这里的函数前面写@wraps(func_name)
  3. 用户可以用help()来查看方法的帮助,避免露馅需要修复
  1. 无修复状态
def outer(func_name):
    def inner(*args, **kwargs):
        """我会让你懵"""
        res = func_name(*args, **kwargs)
        return res

    return inner


@outer
def func1():
    """我是func1"""
    pass


help(func1)

Help on function inner in module __main__:

inner(*args, **kwargs)
    我会让你懵


  • 修复完成后
from functools import wraps


def outer(func_name):
    @wraps(func_name)
    def inner(*args, **kwargs):
        res = func_name(*args, **kwargs)
        return res

    return inner


@outer
def func1():
    """我是func1"""
    pass


help(func1)

Help on function func1 in module __main__:

func1()
    我是func1
    
  • 递归函数

  • 递归含义

python 官方规定最大递归数大概在1000左右
递归的含义:就是想找到某个值,需要通过中介取不断地向目标值寻找靠近,直到找到为止


# 递归1(非递归函数)
def func():
    print('hahah')
    return func()

func()

# 递归二(非递归函数)
def func1():
    print('hahah')
    return func2()

def func2():
    print('xixi')
    return func1()

func1()
func2()

  • 递归函数

回溯的含义: 找到值后一层一层的返回给递归开始之初的地方
递归函数:就是有递归和回溯,并且递归的难度一层比一层低

# 找年纪的递归函数
# 第5个人的年纪是18岁,第5个人比第4个人小两岁,依次类推,求第一个人的年纪

def func1(n):
    if n == 1:
        return 18
    return func1(n - 1) + 2


res = func1(5)
print(res)

联系题

1.利用递归函数依次打印列表中每一个数据值
l1 = [1,[2,[3,[4,[5,[6,[7,[8,]]]]]]]]

l1 = [1, [2, [3, [4, [5, [6, [7, [8, [9, ]]]]]]]]]

def index(l1):
    for i in l1:
        if type(i) == type(l1):
            index(i)
        else:
            print(i)


index(l1)

联系题


#   基本要求
#    	 执行每个函数的时候必须先校验身份 eg: jason 123

userwesley = 'wesley'
password = '123'

while True:
    print("""
    1. 注册功能
    2. 登录功能
    3. 转账
    4. 退出
    """)

    def outer(func_name):
        def inner(*args, **kwargs):
            user = input('请输入登录账户:')
            pwd = input('请输入密码: ')
            if user == userwesley and pwd == password:
                res = func_name(*args, **kwargs)
            else:
                print('当前无权限')
            return res
        return inner

    @outer
    def register():
        print('注册用户')

    @outer
    def login():
        print('用户登录')

    @outer
    def transfer():
        print('转账')

    @outer
    def withdraw():
        print('退出')


    dict1 = {'1': register,
             '2': login,
             '3': transfer,
             '4': withdraw
             }
    info = input('请输入需要使用的功能: ')
    print(info)
    dict1.get(info)()
posted @ 2025-03-13 13:41  樵夫-  阅读(10)  评论(0)    收藏  举报