zoukankan      html  css  js  c++  java
  • 闭包函数与装饰器

    闭包函数

    1、闭包函数必须在函数内部定义
    2、闭包函数可以引用外层函数的名字
    闭包函数是 函数嵌套、函数对象、名称空间与作用域结合体
    # 直接传参
     
    def func(x):
    print(x)
    func(1000)
    # 通过闭包函数传参
    # 通过闭包函数传参
    def outer(number):
    # inner就是闭包函数
    def inner():
    print(number)
     
    return inner
     
     
    func = outer(1000) # --> inner地址 -->func变量名
    func() # func ---> inner地址()

    闭与包

    基于函数对象的概念,可以将函数返回到任意位置去调用,但作用域的关系是在定义完函数时就已经被确定了的,与函数的调用位置无关
    也就是说函数被当做数据处理时,始终以自带的作用域为准,若内嵌函数包含对外部函数作用域(而非全局作用域)中变量的引用,那么该‘内嵌函数’就是闭包函数,简称闭包(closures)

    闭包函数的用途

    目前为止,我们得到了两种位函数体传值的方式,一种是直接以参数的形式传入,另外一种就是将值包给函数
    对比两种方式,方式一在下载同一页面时需要重复传入url,而方式二只需要传一次值,就会得到一个包含指定url的闭包函数,以后调用该闭包函数无需再传url
    闭包函数的应用实验
    需求:爬取某个网站,打印获取数据的长度
    爬虫是获取数据
    方式一:直接传参
    import requests # 调用requests包
     
     
    def spider_func(url):
    # 往url地址发送请求,获取响应数据
    response = requests.get(url)
    # 状态码200
    if response.status_code == 200:
    print(len(response.text))
    # print(response.text)
     
     
    spider_func(url)
     
    23357
    方式二:通过闭包函数接受url地址,执行爬取函数
    def spider_outer(url):
    def spider_inner():
    response = requests.get(url)
    if response.status_code == 200:
    print(len(response.text))
     
    return spider_inner
     
     
    spider_blog = spider_outer('https://www.cnblogs.com/xiaoyuanqujing/')
     
    spider_blog()
     
    23357

    装饰器

    为何要用装饰器?
    软件的设计应该遵循开放封闭原则,即对扩展是开放的,而对修改时封闭的,对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况,对修改封闭,意味着对象一旦设计完成,就可以独立完成其工作,而不要对其进行修改
    软件包含有的所有功能的源代码以及调用方式,都应该避免修改,否则一旦改错,则极有可能产生连锁反应,最终导致程序崩溃,而对于上线后的软件,新需求或者变化又层出不穷,我们必须为程序提供扩展的可能性,这就用到了装饰器
    什么是装饰器?
    ‘装饰’代指为被装饰对象添加新的功能,‘器’代指器具/工具,装饰器与被装饰的对象均可以是任意调用对象,概括的讲,装饰器的作用就是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能,装饰器经常用于有切面需求的场景,如:插入日志、性能测试、事物处理、缓存、权限校验等应用场景,装饰器是解决这类问题的绝佳设计,有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用
    提示:可调用对象有函数,方法或者类,此外我们单以本章主题函数为例,来介绍函数装饰器,并且被装饰的对象也是函数
    总结:
    不修改被装饰对象的源代码
    不修改被装饰对象的调用方式
    被装饰对象:---->需要添加功能,函数
    装饰器:----> 被装饰对象添加的新功能 函数

    统计电影的下载时间

    import time
     
    def download_movie():
    print('开始下载电影...')
    # 模拟电影下载时间 3秒
    time.sleep(3) # 等待3秒
    print('电影下载成功...')
     
     
    start_time = time.time() # 获取当前时间戳
    download_movie()
    end_time = time.time() # 获取当前时间戳
    print(f'消耗时间: {end_time - start_time}')
     
    开始下载电影...
    电影下载成功...
    消耗时间: 3.0001180171966553
    装饰器
    def download_movie():
    print('开始下载电影...')
    # 模拟电影下载时间 3秒
    time.sleep(3) # 等待3秒
    print('电影下载成功...')
     
     
    # start_time = time.time() # 获取当前时间戳
    # download_movie()
    # end_time = time.time() # 获取当前时间戳
    # print(f'消耗时间: {end_time - start_time}')
     
     
    # 装饰器:
     
    def time_record(func):
    def inner():
    # 统计开始
    start_time = time.time()
    func() # func() ---> download_movie()
    # 当被统计的函数执行完毕后,获取当前时间
    end_time = time.time()
    # 统计结束,打印统计时间
    print(f'消耗时间: {end_time - start_time}')
     
    return inner
     
     
    inner = time_record(download_movie) # inner
    inner() # inner() ---> download_movie()
     
     
    开始下载电影...
    电影下载成功...
    消耗时间: 3.0001163482666016
    问题1:被装饰的对象有返回值
    def download_movie():
    print('开始下载电影...')
    # 模拟电影下载时间 3秒
    time.sleep(3) # 等待3秒
    print('电影下载成功...')
    return '小夏.mp4'
     
     
    def time_record(func): # func <-- download_movie
    # 在闭包函数中
    def inner():
    # 统计开始
    start_time = time.time()
    res = func() # func() ---> download_movie()
    # 当被统计的函数执行完毕后,获取当前时间
    end_time = time.time()
    # 统计结束,打印统计时间
    print(f'消耗时间: {end_time - start_time}')
    return res
     
    return inner
     
     
    download_movie = time_record(download_movie)
    download_movie()
     
     
    开始下载电影...
    电影下载成功...
    消耗时间: 3.000156879425049
    问题2 被装饰的对象有参数
    def download_movie(url):
    print(f'{url}中的电影开始下载了...')
    # 模拟电影下载时间 3秒
    time.sleep(3) # 等待3秒
    print('电影下载成功...')
    return '小夏.mp4'
     
     
    def time_record(func): # func <-- download_movie
    # 在闭包函数中
    def inner(url):
    # 统计开始
    start_time = time.time()
    res = func(url) # func() ---> download_movie()
    # 当被统计的函数执行完毕后,获取当前时间
    end_time = time.time()
    # 统计结束,打印统计时间
    print(f'消耗时间: {end_time - start_time}')
    return res
     
    return inner
     
     
    download_movie = time_record(download_movie)
    download_movie('https://www.baidu.com')
     
     
    https://www.baidu.com中的电影开始下载了...
    电影下载成功...
    消耗时间: 3.000974178314209
    问题3:被装饰的对象有多个参数
    def download_movie(url):
    print(f'{url}中的电影开始下载了...')
    # 模拟电影下载时间 3秒
    time.sleep(3) # 等待3秒
    print('电影下载成功...')
    return '小夏.mp4'
     
     
    def time_record(func): # func <-- download_movie
    # 在闭包函数中
    def inner(*args, **kwargs):
    # 统计开始
    start_time = time.time()
    res = func(*args, **kwargs) # func() ---> download_movie()
    # 当被统计的函数执行完毕后,获取当前时间
    end_time = time.time()
    # 统计结束,打印统计时间
    print(f'消耗时间: {end_time - start_time}')
    return res
     
    return inner
     
     
    download_movie = time_record(download_movie)
    download_movie('https://www.baidu.com')
     
    https://www.baidu.com中的电影开始下载了...
    电影下载成功...
    消耗时间: 3.0000598430633545
    装饰器的模板
    def wrapper(func):
    def inner(*args, **kwargs):
    return inner()

    装饰器的语法糖

    装饰器的语法糖,是属于装饰器的
    @:装饰器的语法糖
    注意:在使用装饰器语法糖时,装饰器必须定义在被装饰对象之上
    import time
     
     
    # 统计函数执行时间装饰器
    def wrapper(func): # 被装饰对象
    def inner(*args, **kwargs): # 被装饰对象的参数
    # 调用前增加新功能
    start_time = time.time()
    # 调用被装饰对象,并接收返回值
    res = func(*args, **kwargs)
     
    # 调用后添加新功能
    end_time = time.time()
    print(end_time - start_time)
     
    return res
     
    return inner
     
     
    # func函数需要执行3秒
     
    # 无参装饰器
    # 使用装饰器
    @wrapper # wrapper(func) ---> func
    def func():
    time.sleep(3)
     
     
    # 不用装饰器
    def func2():
    time.sleep(3)
     
     
    func()
     
     
    3.0001182556152344
     
  • 相关阅读:
    Leetcode 538. Convert BST to Greater Tree
    Leetcode 530. Minimum Absolute Difference in BST
    Leetcode 501. Find Mode in Binary Search Tree
    Leetcode 437. Path Sum III
    Leetcode 404. Sum of Left Leaves
    Leetcode 257. Binary Tree Paths
    Leetcode 235. Lowest Common Ancestor of a Binary Search Tree
    Leetcode 226. Invert Binary Tree
    Leetcode 112. Path Sum
    Leetcode 111. Minimum Depth of Binary Tree
  • 原文地址:https://www.cnblogs.com/everuse/p/11843027.html
Copyright © 2011-2022 走看看