zoukankan      html  css  js  c++  java
  • 函数的装饰器

    闭包函数,
    闭包函数 = 函数嵌套定义+ 函数对象+ 名称空间与作用域
    闭: 指的是该函数时定义在一个函数内部的函数
    包; 指的是该函数访问了一个来之于外层函数的变量

    例:
    def func():
    x = 3
    def func():
    print(x)
    这就是闭包函数, 把x 封闭到局部空间中

    为函数体传参:
    1. 直接使用参数的形式
    def wrapper(x):
    print(x)
    wrapper(2)

    2. 把函数体想要的参数包给它
    def outter(x):
    # x = 111
    def wrapper():
    print(x)
    return wrapper # 把函数wrapper返回到全局作用域中
    调用时可以在全局调用
    闭包函数实现了变量定义在函数内代码只能内部访问, 二外部访问不到


    装饰器
    什么是装饰器
    装饰器就是装饰函数的一种功能, 对原函数进行装饰
    装饰器的定义, 在不改变源代码情况下, 为函数添加新功能,
    函数体和调用方式都不能改变
    开放封闭原则

    1. 被装饰函数index
    def index(x,y):
    print(x,y)
    index(1,2)

    2. 为index添加新功能 '运行时间计时'
    import time
    def index(x,y):
    start = time.time()
    time.sleep(1)
    ends = time.time()
    print(ends-start)
    index(1,2)
    改变了源代码, 不可行

    3. def index(x,y):
    print(x,y)

    start = time.time()
    index(1,2)
    ends = time.time()
    print(ends-start)
    没改变源代码,也没改变调用方式
    但执行起来麻烦, 如果有多处需要修改, 那么就很麻烦了

    4. 定义一个装饰函数, 把函数放到装饰器内
    原:
    def index(x,y):
    print(x,y)
    def wrapper():
    start = time.time()
    index(1,2)
    ends = time.time()
    print(ends-start)
    wrapper()
    功能实现了, 但调用方式也跟着改变了, 不可行

    5. 把wrapper函数内的index修改为参数形式
    def index(x,y):
    print(x,y)

    def wrapper(func):
    start = time.time()
    func(1,2)
    ends = time.time()
    print(ends-start)
    wrapper(index)
    直接为wrapper函数传参的方式不行, 因为index是在全局, 而现在把index放到
    局部中, 调用时还得先调用wrapper, 显然不符合, 不可行

    6. 基于闭包函数吧wrapper函数想要传参包给index, 然后基于函数对象
    把值也包给index,
    def index(x,y):
    print(x,y)
    def wrapper(func):
    def inner():
    start = time.time()
    func(1,2)
    ends = time.time()
    print(ends-start)
    return inner
    index = outter(index)
    index()
    这样大致的模仿传来传参方式了, 但如果我想把函数内的参数改变呢
    还是不行, 继续改,

    7. 这里使用到* 和 ** 的知识点了, 把func传参位置换成*和**

    def index(x,y):
    print(x,y)
    def outter(func):
    def wrapper(*args,**kwargs):
    start = time.time()
    func(*args,**kwargs)
    ends = time.time()
    print(ends -start)
    return wrapper
    index(1,2)
    完美保证了不该源代码和调用方式,而且还可以装饰任意函数

    这样就完善了, 怎么传参都行, 但是有没有想到, 如果函数有返回值呢,
    作何处理, 下面处理

    8. 在前方案改进, 把wrapper函数的返回值与被装饰函数报持一致
    def index(x,y):
    print(x,y)
    return 123

    def wrapper(func):
    def inner(*args,**kwargs):
    start = time.time()
    res = func(*args,**kwargs)
    ends = time.time()
    print(ends-start)
    return res
    return inner
    ret = index(1,2)
    print(ret)
    这样既可不改源代码, 又没有改变调用方式,
    基本装饰器就这么实现了

    标准装饰器如下
    def wrapper(func):
    def inner(*args,**kwargs):
    """功能代码"""
    res = func(*args,**kwargs)
    """功能代码"""
    return res
    return inner


    语糖的使用
    @装饰器

    @wrapper
    def func():
    print('only')

    了解:
    文档注释,
    把原函数的注释和文档一并拷贝到被装饰的函数中
    用到functools 模块中的wraps
    使用方法
    在装饰器内接受函数的变量名头顶上加@wraps
    例:
    def wrapper(func):
    @wraps # 完美
    def inner(*args,**kwargs):
    start = time.time()
    res = func(*args,**kwargs)
    ends = time.time()
    print(ends-start)
    return res
    return inner


    叠加多个装饰器
    例:
    def deco1(func1):
    def inner():
    print(func1):
    return inner

    def deco2(func2):
    def inner():
    print(func2)
    return inner

    @deco1
    @deco2
    def index(x):
    print(x)

    多个装饰器叠加的方式, 直接在函数头顶加
    简单概述
    当程序运行遇到语法糖@ 是, 语法糖函数会马上把@下的函数名
    返回给 '@函数' 然后运行监测 '@函数' ;但不会马上运行函数体代码
    结论
    加载顺序从下往上
    执行顺序自上往下内层的函数


    # 2、叠加多个装饰器
    # 2.1 加载顺序:自下而上
    # 2.2 执行顺序:自上而下运行内层的wrapper函数

    # def deco1(func1): # func1 = wrapper2的内存地址
    # def wrapper1(*args,**kwargs):
    # print('wrapper1====>')
    # res1=func1(*args,**kwargs)
    # return res1
    # return wrapper1
    #
    # def deco2(func2): # func2 = wrapper3的内存地址
    # def wrapper2(*args,**kwargs):
    # print('wrapper2====>')
    # res2=func2(*args,**kwargs)
    # return res2
    # return wrapper2
    #
    # def deco3(func3): # func3 = 最原始的那个被装饰函数的内存地址
    # def wrapper3(*args,**kwargs):
    # print('wrapper3====>')
    # res3=func3(*args,**kwargs)
    # return res3
    # return wrapper3
    #
    # # index=wrapper1的内存地址
    # @deco1 # deco1(wrapper2的内存地址)=>wrapper1的内存地址
    # @deco2 # deco2(wrapper3的内存地址)=>wrapper2的内存地址
    # @deco3 # deco3(最原始的那个被装饰函数的内存地址)=>wrapper3的内存地址
    # def index(x,y):
    # print('index=>',x,y)
    #
    # index(1,2)
    #
    结果
    # """
    # wrapper1====>'
    # wrapper2====>
    # wrapper3====>
    # index=>1,2
    # """

  • 相关阅读:
    python框架---->APScheduler的使用
    python基础---->python的使用(六)
    python爬虫---->scrapy的使用(一)
    python基础---->python的使用(四)
    python基础---->python的使用(一)
    网页mp3播放代码
    js打开没有地址栏下拉条新窗口
    php全面获取url地址栏及各种参数
    php分页类
    php生成随机密码的几种方法
  • 原文地址:https://www.cnblogs.com/Knge/p/13356316.html
Copyright © 2011-2022 走看看