zoukankan      html  css  js  c++  java
  • python 装饰器

    装饰器

    装饰器其实是一个函数,作用是装饰其他函数

    装饰器的特点:

    1. 不改变被装饰的函数的源代码的情况下添加函数的功能
    2. 不改变被装饰的函数的调用方式
    3. 装饰器的组成方式:高阶函数+嵌套函数

    高阶函数

    以一个函数名(函数内存地址)为参数,此类函数就是高阶函数

    譬如:

    print(abs(-10))  # abs,内置函数,返回一个数的绝对值
    
    def test(num,func):  # 高阶函数,以一个函数名作为参数,传递给另一个函数
        absNum = func(num)  # 调用函数
        print(absNum)
    
    test(-2,abs)     # 注意,这里写的是abs,是函数名,也就是函数的内存地址
    

    嵌套函数

    一个函数内部,可以嵌套函数

    def outer():
        def inner():  # 声明一个内部函数
            x = 2
            print(x)
        inner()  # 执行内部函数
    
    outer()
    

    闭包

    一个内嵌函数,引用了外层函数的变量,并且外层函数的返回值是内嵌函数。这就实现了一个闭包。

    说白了,内嵌函数没有重新声明某个外层变量,直接使用外层函数的变量,依靠上述这种闭包形式,可以让内嵌函数和外层函数中的变量绑定,延伸了变量的作用域

    def out():
        x = 1
        def inner():
            print(x)  # inner 没有定义 x,引用了外层函数中的 x 变量
    
        return inner  # 返回 inner 函数
    
    f = out()  # f == inner,f现在是全局变量,相当于把 inner 函数提取到全局变量中来了
    f()  # 相当于调用了 inner(),虽然 inner 中没有定义x,但是依然能打印出x的内容,说明 inner 和外层的 x 变量绑定了。
    

    无参数的装饰器

    下面的例子实现了一个 计时器 的装饰器,可以统计一个函数的执行时间

    import time
    
    
    def timer(func):  # func 是个函数
        def inner(*args,**kwargs):
            time1 = time.time()
            res = func(*args,**kwargs)  # 执行传递过来的 func 函数
            time2 = time.time()
            print(time2 - time1)
            return res
        return inner
    
    @timer  # python 的语法糖,会把被装饰的函数作为参数,相当于:test = timer(test); 
    def test():
        time.sleep(2)
        print('func done.')
    
    
    test()
    
    """
    1. 执行 test(),发现有装饰器语法,默认执行 test = timer(test)
    2. 执行 timer(test),返回值是 inner
    3. test == inner
    4. 所以 test() == inner()
    5. 执行 inner()
    """
    

    有参数的装饰器

    import time
    
    
    def outer(name):  # 有参数装饰器, 多包了一层函数
        def timer(func):
            def inner(*args,**kwargs):
                print(name)
                time1 = time.time()
                res = func(*args,**kwargs)  # 执行传递过来的 func 函数
                time2 = time.time()
                print(time2 - time1)
                return res
            return inner
        return timer  # 多包一层函数,也就多一个返回值
    
    @outer("wang")  # 先执行 outer("wang"),返回timer, 再执行 @timer,test = timer(test)
    def test():
        time.sleep(2)
        print('func done.')
    
    test()
    
    """
    1. 执行 test(), 发现装饰器语法,先执行 @outer("wang")
    2. outer("wang") 返回值是 timer
    3. 所以相当于 @timer,此处就和无参装饰器一样了,执行 test = timer(test)
    4. ...
    """
    

    @wraps

    上面的例子我们知道,装饰后的函数,实际上执行的是装饰器内层的函数inner,因此,我们如果执行print(test.__name__), 得到的结果其实是:inner@wraps 可以让装饰后的函数,属性和被装饰前完全一致。

    import time
    from functools import wraps
    
    
    def timer(func):
        @wraps(func)  # 将 func 的属性,赋值给 inner
        def inner(*args,**kwargs):
            time1 = time.time()
            res = func(*args,**kwargs)  # 执行传递过来的 func 函数
            time2 = time.time()
            print(time2 - time1)
            return res
        # inner.__name__ = func.__name__  # 上面 @wraps(func) 实际做的就是这件事情
        # inner.__doc__ = func.__doc__
        return inner
    
    @timer
    def test():
        time.sleep(2)
        print('func done.')
    
    print(test.__name__)  # 结果是:test
    
    

    变量作用域

    python中,每个变量都有自己的作用域。常见的作用域有:局部作用域 > 外层作用域 > 全局作用域 > python内置作用域。

    作用域示例:

    x = 1  # 声明在模块级别,全局变量
    
    def outer():
        x = 2  # 声明在函数内部,局部变量
    
        def inner():
            print("inner:",x)  # inner函数内部没有声明x变量,在上级作用域查找
    
        inner()
        print("outer:",x)
    
    outer()
    print("global:",x)
    
    
    """
    inner: 2
    outer: 2
    global: 1
    """
    

    global 关键字

    在函数内部使用 global 关键字,可以将局部变量提升成全局变量

    x = 1  # 全局变量
    
    def func():
        global x  # 在函数内部,global 关键字说明 x 是全局变量,不再局限于函数内部。
        x = 2  # x 现在是全局变量,此处重新赋值
        print(x)
    
    func()
    print(x)  # 执行完 func(), 全局的 x 也变了
    
    
    """ 结果:
    2
    2
    """
    

    nonlocal 关键字

    此关键字,可以让内嵌函数仅使用它的外层变量,而非全局变量。

    x = 0  # 全局
    def outter():
        x =  1  # 内嵌函数的外层变量
        def inner():
            nonlocal x  # 告诉程序,这是使用了外层的 x
            x = 2
    
        inner()  # 执行完 inner
        print(x)  # x 会变成 2
    
    outter()
    print(x)  # 全局的 x 依然是0
    
  • 相关阅读:
    webpack的入门实践,看这篇就够了
    vue系列教程-15vuex的使用
    vue系列教程-14axios的使用
    vue系列教程-13vuecli初体验
    vue系列教程-12vue单文件组件开发
    vue系列教程-11vuerouter路由
    vue系列教程-10vue过滤器和自定义指令
    vue系列教程-09vue组件
    Spring Redis开启事务支持错误用法导致服务不可用
    MySQL主从数据库配置与原理
  • 原文地址:https://www.cnblogs.com/wztshine/p/11765760.html
Copyright © 2011-2022 走看看