装饰器基础知识

今日内容概要

  • 必包函数

  • 必包函数的实际应用

  • 装饰器简介

  • 简易版本装饰器

  • 进阶版本装饰器

  • 完整版本装饰器

  • 装饰器模板

  • 装饰器语法糖

  • 装饰器修复技术

     

 

今日内容详细

一、闭包函数

# 闭包函数的两大特征
  1.闭:是指定义在函数内部的函数
  2.包:是指在内部函数使用了外层函数名称空间中的名字
例题:
def outer():
   x = 999
   def index():
       print('要好好学习,x')
    return index
# 结果是999,如果是局部名称空间内层函数在内层中没有其他的值,该函数需要往局部外层找x对应的值,只能遵循从局部>>>全局>>>>内置去找
如果在全局上再来一个变量名,也是一样的:
x = 666,结果还是999
res = outer()
res()

 

 

 

 

 

二、闭包函数的实际应用

# 闭包函数是给函数体传参的另外一种方式
# 函数体传参的方式有两种:
 1.通过形参  
   def index(username):
       print(username)
   index('jason')  # jason
 函数体代码需要什么就可以在形参中写什么

 2.通过闭包函数的方式:
   def outer():
   username ='jason'
   def index():
       print(username)  # 通过从内层往外层传参 从而达到传参的作用 缺点:是username ='jason'这里会限制一个具体的值 不适于多个具体的值
   return index
  res =outer()
  res()  # jason
 
   例题2:
   def outer(username):
   # username ='jason' (可以将这步省略掉就可以出现多个值可以使用,比较灵活)
   def index():
       print(username)
   return index
res =outer('kevin')    # 只需要在括号内换值即可
res()

 

 

 

 

三、装饰器简介

# 装饰器并不是一个新的知识点 而是函数基础知识整合到一起的产物

装饰器的本质
   在不改变被装饰器对象原有的“调用方式”和“内部代码”的情况下给被装饰对象添加新的功能
   
   装饰器的两大原则:
       对扩展开放
       对修改封闭
       
#   引入装饰器前的前戏的小概念:时间戳
 # 时间戳:是指1970年1月1日0时0分0秒距离刚刚代码运行间隔的秒数
 # 时间戳相应的代码格式:
   import time
   print(time.time())
# time.sleep(3) # 表示的是让程序原地等待三秒
   print('hello world')
需求 #:如何统计函数运行一个体代码的执行时间:思路是只要在开始时检测一下时间 然后在函数体代码结束后再检测一次时间 最后两个时间的差值就是函数体代码运行的时间
import time
def index():
   time.sleep(3)
   print('from index')
   # 下面再给index函数增加一个统计执行时间的功能
start_time = time.time()  # 函数执行之前获取一个时间戳
index()
end_time =time.time()  # 函数执行之后获取一个时间戳
print(end_time-start_time)  # 两个时间戳的差值就是这个函数运行的执行时间
# from index
# 3.0001718997955322

 

 

 

 

 

四、简易版本装饰器

简易版本装饰器的推导过程:
import time
# def index():
#     time.sleep(1)
#     print('from index')
'''给index函数增加了一个统计执行时间的功能'''
# start_time = time.time() # 函数执行之前获取一个时间戳
# index()
# end_time = time.time() # 函数执行之后获取一个时间戳
# print(end_time - start_time) # 两个时间戳的差值就是函数的执行时间

思考:1.如果在我们需求中很多地方都要调用index 如何统计index的执行时间呢?
思路:我们在想在很多地方都需要执行统计index函数执行时间的代码>>>>就是在不同的地方需要执行相同的代码>>>>然后联想到函数的定义>>>>把统计执行时间这个功能代码做到函数不就可以了
def get_time(func):
   start_time =time.time()
   func()
   end_time = time.time()
   print(end_time-start_time)
import time
def home():
   time.sleep(3)
   print('from home')
get_time(index)
get_time(home)
如果是通过传参的方式:
   缺陷1:
        代码写死了 无法统计其他函数的执行时间
        解决方式:将函数名通过形参的形式传入
   缺陷2:
        调用方式改变了:封装成函数后 调用方式由index()变成get_time() 调用方式改变了 不符合装饰器原则
        解决方式:暂时没有解决方式
  所以通过传参的形式行不通

因采用直接给函数体传参的方式无法实现装饰器的原则,所以只能采用第二种用闭包函数的形式给函数传参
import time
def index():
   time.sleep()
   print('from index')
def outer(func):  # 真正的index被outer局部名称空间中的index取代了
   def get_time():
       start_time = time.time()  # 函数执行前获取的一个时间戳
       func()  # 调用了真正的index函数
       end_time = time.time()  # 函数执行后获取的一个时间戳
       print(end_time-start_time)  # 两个时间戳的差值就是函数的执行时间
       return get_time
res = outer(inder)  # 左侧的res就是一个普通的变量名
res()            
因变量名是可以随便取的,所以我们当然也可以将左边的变量名取为真的变量名 这样就实现给函数传参的作用(实现了狸猫换太子的作用 结果就是:表面上还是那个变量名 其实质已由全局换到局部中去了)
故有:
index =outer(index)  
index()    # 表面上是调用了index 实际调用了get_time
print(index) # 全局名称空间中的index指向的是get_time函数体代码


   

 

 

 

 

五、进阶版本装饰器

# 解决的是参数问题
def outer(func_name):
  def get_time(*args, **kwargs):   # 只有在这里才将参数接收 有就接收 没有也没关系
      start_time = time.time()
      func_name(*args, **kwargs) # 只有在这里才将参数接收 有就接收 没有也没关系
      end_time = time.time()
      print(end_time - start_time)
  return get_time
  需要用到可变长参数的原因是:因为在这题目调用局部函数括号内两个都没有参数,所以如果局部函数定义阶段内有参数,函数就不能运行,所以需要运用可变长参数来接收,反之,在调用函数局部时有参数时,局部函数运用可变长函数也是可以接收了,总之为了避免调用参数的问题,所以运用可变长参数来灵活处理

 

 

 

六、完整版本装饰器

# 解决的是返回值问题
def outer(func_name):
   def get_time(*args, **kwargs):
       start_time = time.time()
       res = func_name(*args, **kwargs)  # 执行真正的index函数
       end_time = time.time()
       print(end_time - start_time)
       # return '不要急躁' # 如何在此处返回真正index函数的返回值
       return res
   return get_time

 

 

 

 

 

七、装饰器模板(重要)

# 编写装饰器其实有一套固定的代码 不需要做任何理解
def outer(func_name):   # func_name用于接收被装饰的对象(函数)
   def inner(*args,**kwargs):
       print('执行被装饰函数之前 可以做的额外操作')
       res = func_name(*args,**kwargs)  # 执行真正的被装饰函数
       print('执行被装饰函数之后 可以做的额外操作')
       return res # 返回真正函数的返回值
   return inner

 

 

 

 

 

八、装饰器语法糖

# 仅仅是让代码编写的更加好看、简洁!!!

def outer(func_name):
   def inner(*args,**kwargs):
       print('执行函数之前的操作')
       res = func_name(*args,**kwargs)
       额外操作
       return res
   return inner
@outer  # 等价于 index = outer(index)
def index(*args,**kwargs):
   print('from index')
  # index = outer(index) # 这样书写显得代码不好看

语法糖内部原理
 1.使用的时候最好紧跟在被装饰对象的上方
 2.语法糖会自动将下面紧挨着的函数名传给@后面的函数调用

 

 

九、装饰器修复技术

# 目的是要做到比真的还要真 但是其本质还是没有变
from functools import wraps
def outer(func_name):
   @wraps(func_name)
   def inner(*args,**kwarga):
       print('执行前被装饰对象之前可以做的操作')
       res = func_name(*args,**kwargs)
       return res
   return inner
@outer
def index():
   print('from index')
@outer
def home():
   '''这是对home函数的解释'''
   print('from home')
# help(home) # help可以查看指定函数的注释信息
# help(len)
# index() # 这样的形式存在瑕疵 还是可以让人发现是假的
# print(index)
# help(home)
# help(index)
   

 

posted @   一颗平凡的小石头  阅读(66)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示