【详解】Python-----------闭包和装饰器

本文详细探讨了Python中的闭包概念,包括定义、实现条件和应用场景。接着,文章深入介绍了装饰器,从定义、实现到应用,特别是装饰器在函数功能增强中的角色。此外,还讲解了如何保留被装饰函数的原有帮助信息以及如何处理多个装饰器的使用情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

闭包的定义与实现

1)定义
闭包就是指有权访问另一个函数作用于中的变量的函数,具有提高代码可复用性的作用。

2)实现闭包的三个条件
闭包的另一个常用场景就是装饰器

 - 函数里面嵌套函数
 - 内部函数使用了外部函数的临时变量
 - 外部函数的返回值是内部函数的引用

验证闭包的实现条件

1)代码

# 定义函数不执行函数里面的代码,调用函数和时才执行函数里面的代码
# 所以次代码返回值时wrapper
def timeit(name):
    # wapper 包装纸
    def wapper():
        print(name)     # 内部函数使用外部函数的变量
        return 'ok'
    return wapper       # 外部函数的返回值是内部函数的引用(函数名)

result = timeit("lyqiu")
print(result)

2)结果展示
调用timeit函数,函数的返回值是wrapper函数引用,上面代码中的result就是wrapper函数
在这里插入图片描述

调用函数

1)代码

# 方法1
result1 = result()      # result的结果是wapper的函数名,此处等同于result1 = wapper()进行函数的调用
print(result1)

# 方法2
print(result())		#直接在result = timeit('lyqiu') 后打印输出result()

在这里插入图片描述

2)结果展示
在这里插入图片描述

装饰器的定义

  • 器指的是工具,而程序中的函数就是具备某一功能的工具,所以装饰器指的是为被装饰器对象添加额外功能的工具/函数,装饰器就是用来装饰函数或类的工具
  • 装饰器其实就是在遵循以下两个原则(开放封闭原则)的前提下为被装饰对象添加新功能
开放封闭
对扩展开放对已经实现的功能代码块封闭,不修改被装饰对象的源代码

装饰器的实现

 - 装饰器本质上是一个函数,该函数是用来处理其他函数。
 - 它可以让其他函数在不需要求改代码的前提下增加额外的功能
 - 并且装饰器的返回值也是一个函数对象
 - 装饰器装饰的是函数/类,所以需要传递的参数是函数名或者类名
 - 使用时:  @装饰器名字

装饰器的应用(一)

分为创建装饰器和使用装饰器这两部分
通过下载音乐的练习,使我们对装饰器的使用有一个初步了解

1)实验前提

  • pip3 install requests -i https://pypi.douban.com/simple安装
    在这里插入图片描述
  • 为计算下载时间可以使用时间戳
    在这里插入图片描述

2)代码

import time        #时间处理模块
import requests		#HTTP请求库,多用于网络爬虫,需要pip install下载

# 1.如何去创建装饰器
#需求:添加功能-计算被装饰函数运行的时间的工具
def timeit(f):
    def wrapper():      #添加被装饰函数执行的时间
        start = time.time()		#函数执行之前计算当前的时间戳
        result = f()			#调用被装饰的函数,并保存函数的返回值
        end = time.time()		 #函数执行后计算当前的时间戳
        print("函数%s执行使用的时间是%.3fs"%(f.__name__,end-start))
        return result
    return wrapper
    
# 2.如何去使用装饰器
@timeit
def download_music(): 				            
	url="http://m10.music.126.net/20200719111612/e36c0e235dbad219e9d8f0e65fa62007/ymusic/0201/7233/bea2/2cb43c8bcaa7797d32e5ca9b831350d8.mp3"
    response = requests.get(url)	 #模拟浏览器访问mp3的网址,获取服务器端给我们的响应(response)
    #music_content = response.content    #获取mp3音乐的内容
    with open("再见.mp3", "wb") as f:    #打开文件,存储音乐内容到文件中
    	#f.write(music_content)
        f.write(response.content)
    print("再见.mp3下载完成......")

download_music()

在这里插入图片描述
3)解释装饰器的使用原理

执行的过程:
	 - timeit(download_music)函数的返回值时wraper函数名。
	 - download_music=timeit(download_music),让download_music指向wrapper函数
	 - 最后一行downliad_music(),实质上是执行的函数wrapper()
	 - 执行wrapper函数时,f()实质上执行的函数download_music()

4)结果展示
在这里插入图片描述

装饰器的应用(二)

1)代码

import time        #时间处理模块

def timeit(f):
    def wrapper(*args, **kwargs):
        """
    
            # *wargs和**kwargs是形参 (定义函数是形参)
            # *args:可变参数,可以接受多个参数信息,一般存储到元祖中
            # **kwargs:关键字参数,可以接受多个键值对信息,一般存储到字典中
            # wrapper(10, 20)接收时用元组来存储(10, 20)

        """
        start = time.time()
        result = f(*args, **kwargs)
        """

            # 调用被装饰的函数,并保存函数的返回值
            # *args和**kwargs是实参 (调用函数是实参)
            # f(10, 20)==add(10, 20) 要传的是10,20而不是元祖(10,20),所以在上面使用解包
            # 注意:这里*args不是可变参数的意思,是解包的意思,args=(10, 20)是一个元祖;*args=10,20 即把一
个元祖变成两个数值;**kwargs是将一个字典解包成键值对


        """
        end = time.time()
        print("函数%s执行使用的时间是%.3fs"%(f.__name__,end-start))
        return result
    return wrapper

    
#2.如何去使用装饰器
@timeit
def add(num1, num2):
    time.sleep(0.2)		#休眠0.2s
    return num1 + num2
    """
    分析add函数的执行过程:
        1.add(10, 20)调用函数
        2.发现add函数被装饰器timeit装饰了,遇到 @timeit想到其执行原理是 timeit是装饰器,将函数名add作为参数传回去,最后将返回值赋值给函数add 即:add=timeit(add)
        3.timeit(add)函数的返回值是wrapper函数,add=timeit(add)这里add=wrapper
        4.终于知道add函数是什么了,add(20, 20)==>wrapper(10, 20)
        5.在wrapper函数中有f()==>add(10, 20)
        6.返回f()函数的返回值30
    """

print (add(10, 20))


在这里插入图片描述

2)分析执行过程

 分析add函数的执行过程:
     1.add(10, 20)调用函数
     2.发现add函数被装饰器timeit装饰了,遇到 @timeit想到其执行原理是 timeit是装饰器,将函数名add作为参数传回去,最后将返回值赋值给函数add 即:add=timeit(add)
     3.timeit(add)函数的返回值是wrapper函数,add=timeit(add)这里add=wrapper
     4.终于知道add函数是什么了,add(20, 20)==>wrapper(10, 20)
     5.在wrapper函数中有f()==>add(10, 20)
     6.返回f()函数的返回值30

3)结果展示
在这里插入图片描述

编写装饰器的模板

1)模板
以打印日志信息为例

#编写装饰器模板
#   1.先实现闭包
#   2.要让这个装饰器可以装饰所有函数/类(代表汉和函数可以接受有参数/无参数)

import time
def logger(f):
    def wrapper(*args, **kwargs):   #这里是可变参数和关键字参数
        start = time.time()
        result = f(*args, **kwargs)   #这里是解包
        end = time.time()
        print("Logger: %s %s run %.3f s" %(time.ctime(), f.__name__, end-start))  获取当前时间 + f是个变量名,获取其真实名字使用双下划线
        return result
    return wrapper

@logger
def add(num1, num2):
    time.sleep(0.1)		# 为了实验结果更加明显,设置休眠0.1s
    return num1 + num2

result = add(10, 20)
print(result)

2)结果展示
在这里插入图片描述

保留被装饰的函数原有的帮助信息

  • 在上述模板中最后输入print(help(add)),查看add函数的帮助信息。此时出现的是wrapper函数的帮助信息。所以我们需要研究如何让被装饰的函数保持自己原有的帮助信息
    在这里插入图片描述

1)代码

#编写装饰器模板
#   1.先实现闭包
#   2.要让这个装饰器可以装饰所有函数/类(代表汉和函数可以接受有参数/无参数)
#   3.如何让被装饰的函数报出自己原有的帮助信息呢?

import time
from functools import wraps
def logger(f):
    @wraps(f)	 #wraps装饰器用来保留f函数原有的属性,包括他的帮助信息
    def wrapper(*args, **kwargs):	 #这里是可变参数和关键字参数
        """
        wrapper function
        """
        start = time.time()
        result = f(*args, **kwargs)		   #这里是解包
        end = time.time()
        print("Logger: %s %s run %.3f s" %(time.ctime(), f.__name__, end-start))
        return result
    return wrapper


@logger      #add = loggger(add)==> add=wrapper
def add(num1, num2):
    """
    add function
    """
    time.sleep(0.1)
    return num1 + num2

#result = add(10, 20)
#print(result)

print(help(add))

在这里插入图片描述
2)结果展示
在这里插入图片描述

多个装饰器

多个装饰器装饰的时候,从下向上装饰,执行的时候是从上到下进行执行。
1)代码
此时一个是欢迎信息,另一个是打印日志信息

#编写装饰器模板
#   1.先实现闭包
#   2.要让这个装饰器可以装饰所有函数/类(代表汉和函数可以接受有参数/无参数)
#   3.如何让被装饰的函数报出自己原有的帮助信息呢?

import time
from functools import wraps
def welcome(f):
    @wraps(f)	# 保持原有函数的帮助信息
    def wrapper(*args, **kwargs):
        print("welcome......")
        result = f(*args, **kwargs)
        return result
    return wrapper

def logger(f):
    #这里是可变参数和关键字参数
    #wraps装饰器用来保留f函数原有的属性,包括他的帮助信息
    @wraps(f)
    def wrapper(*args, **kwargs):
        """
        wrapper function
        """
        start = time.time()
        #这里是解包
        result = f(*args, **kwargs)
        end = time.time()
        print("Logger: %s %s run %.3f s" %(time.ctime(), f.__name__, end-start))
        return result
    return wrapper

# 多个装饰器装饰的时候,从下向上装饰,执行的时候是从上到下进行执行。
@welcome
@logger      #add = loggger(add)==> add=wrapper
def add(num1, num2):
    """
    add function
    """
    time.sleep(0.1)
    return num1 + num2
result = add(10, 20)
print(result)

在这里插入图片描述
在这里插入图片描述

2)结果展示
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值