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
posted @ 2019-10-30 16:35  wztshine  阅读(166)  评论(0编辑  收藏  举报