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

    一、闭包函数

    1、闭与包

    函数被当做数据处理时,始终以自带的的作用域为准。若内嵌函数包含对外部函数作用域(而非全局作用域)中变量的引用,那么该内嵌函数就是闭包函数

     1 x = 1
     2 def outer():
     3     x = 2
     4     def inner():
     5         print(x)
     6     return inner
     7 
     8 func = outer
     9 func()
    10 # 输出结果为2

    “闭”代表函数是内部的,“包”代表函数外包裹着对外层作用域的引用。闭包函数是函数嵌套、函数对象、名称空间与作用域结合体

    2、特点:

      闭包函数必须在函数内部定义

      闭包函数可以引用外层函数的名字

    二、装饰器

    1、为何要用装饰器

    软件的设计应该遵循开放封闭原则,即对扩展是开放的,而对修改是封闭的。对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展。对修改封闭,意味着对象一旦设计完成,就可以独立完成其工作,而不要对其进行修改

    软件包含的所有功能的源代码以及调用方式,都应该避免修改,否则一旦出错,则极有可能产生连锁反应,最终导致程序崩溃,而对于上线后的软件,新需求或者变化层出不穷,必须为程序提供扩展的可能性,这就用到装饰器

    2、装饰器作用

    在不修改被装饰对象源代码与调用方式的前提下,添加新的功能

    装饰器的定义必须遵循:

      不修改被装饰对象源代码

      不修改被装饰对象调用方式

    3、装饰器的实现

    为下面函数增加一个计时的功能

    1 import time
    2 def download_movie():
    3     print('开始下载电影...')
    4     # 模拟下载需要时间,等待3秒
    5     time.sleep(3)
    6     print('电影下载成功')

    首先想到的可能是:

    1 time_start = time.time()    # 统计开始时间
    2 download_movie()      
    3 time_end = time.time()      # 统计结束时间
    4 print(time_end - time_start)

    如果设计成遵循开放封闭原则,如下

     1 import time
     2 def download_movie():
     3     print('开始下载电影...')
     4     # 模拟下载需要时间,等待3秒
     5     time.sleep(3)
     6     print('电影下载成功')
     7 
     8 def time_record(func):
     9     def inner():
    10         time_start = time.time()    # 统计开始时间
    11         func()      # func() ---> download_movie()
    12         time_end = time.time()      # 统计结束时间
    13         print(f'总共下载时间{time_end - time_start}')  # 函数执行后获取执行时间
    14     return inner
    15 
    16 download_movie = time_record(download_movie)
    17 download_movie()    # download_movie() ---> download_movie()

    这样我们便可以在不修改源代码和调用方式的前提下为其追加统计时间的功能

    但是假如原函数有返回值,这种方法就不能满足要求,可以这样修改:

     1 import time
     2 def download_movie():
     3     print('开始下载电影...')
     4     # 模拟下载需要时间,等待3秒
     5     time.sleep(3)
     6     print('电影下载成功')
     7     return "zzz.mp4"
     8 
     9 
    10 def time_record(func):
    11     def inner():
    12         time_start = time.time()    # 统计开始时间
    13         res = func()      # func() ---> download_movie()
    14         time_end = time.time()      # 统计结束时间
    15         print(f'总共下载时间{time_end - time_start}')  # 函数执行后获取执行时间
    16         return res
    17     return inner
    18 
    19 
    20 download_movie = time_record(download_movie)
    21 download_movie()

    现在又需要增加一种需求,假如原函数有参数传递,但是不知道有多少参数,可以这样修改:

     1 import time
     2 def download_movie(url):
     3     print(f'{url}开始下载电影...')
     4     # 模拟下载需要时间,等待3秒
     5     time.sleep(3)
     6     print(f'电影下载成功')
     7     return "zzz.mp4"
     8 
     9 
    10 def time_record(func):
    11     def inner(*args, **kwargs):
    12         time_start = time.time()    # 统计开始时间
    13         res = func(*args, **kwargs)      # func() ---> download_movie()
    14         time_end = time.time()      # 统计结束时间
    15         print(*args, f'总共下载时间{time_end - time_start}')  # 函数执行后获取执行时间
    16         return res
    17     return inner
    18 
    19 
    20 download_movie = time_record(download_movie)
    21 download_movie()

    至此,我们便实现了一个装饰器的最终效果,它可以扩展原函数的功能,返回值和传递参数。模板可以表示为:

    1 def wrapper(func):
    2     def inner(*args, **kwargs):
    3         # 修改原函数功能代码
    4         res = func(*args, **kwargs)
    5         return res
    6     return inner
    7 
    8 func = warpper(func)
    9 func()

    4、装饰语法糖

    为了简洁而优雅地使用装饰器,python提供了专门的装饰器语法来取代func = warpper(func)的形式,需要在被装饰对象的正上方加上@warpper,当解释器执行到@warper时,就会调用warpper函数,且把被装饰函数名当做参数传入

     1 import time
     2 def wrapper(func):
     3     def inner(*args, **kwargs):
     4         start_time = time.time()
     5         res = func(*args, **kwargs)
     6         end_time = time.time()
     7         print(end_time - start_time)
     8         return res
     9     return inner
    10 @wrapper
    11 def func():
    12     time.sleep(3)
    13 func()
  • 相关阅读:
    selenium+phantomjs报错:Unable to find a free port的分析和解决
    hadoop集群搭建
    虚拟机安装CentOS7 Minimal、jdk和hadoop
    Javascript学习笔记-一些关键点
    隐藏 Win10 中的3D对象、文档、音乐、图片、视频、下载、桌面7个文件夹
    白话网页的网络性能
    (转)“拿人钱财,与人消灾”,这才是员工含义的本质
    JS 小工具 MYSQL WHERE IN条件 去掉换行符(列转行)
    PHP 基于redis的分布式锁
    PHP 将json的int类型转换为string类型 解决php bigint转科学计数法的问题
  • 原文地址:https://www.cnblogs.com/hexianshen/p/11842558.html
Copyright © 2011-2022 走看看