Python中的装饰器

装饰器: 本质就是函数,为其他函数添加附加功能

装饰器原则:

  1. 不修改被装饰函数的源代码

  2. 不修改被装饰函数的调用方式

装饰器=高阶函数 + 函数嵌套 +闭包

1.高阶函数

定义:

  1. 函数接收的参数是一个函数名

  2. 函数的返回值是一个函数名

满足上述任意一个条件的函数即为高阶函数

高阶函数示例

def foo():
    print("hello")

# 函数名作为参数
def func_1(func):
    print("heiheihei")
    func()
    print("world")


# 函数名作为返回值
def func_2(func):
    print("return value: %s" % func)
    return func

# 函数名作为参数和返回值
def func_3(func):
    print("from func_3")
    return func

func_1(foo)
print('----------------')
func_2(foo)
print('----------------')
f = func_3(foo)
f() # f是func_3的返回值,即为函数foo的函数地址,f()执行函数foo输出“hello”

>>heiheihei
hello
world
----------------
return value: <function foo at 0x00000276CAAA3E18>
----------------
from func_3
hello

使用高阶函数实现在不改变函数调用方式的前提下,增加函数的功能

import time

# 为foo函数增加函数运行时间计时功能

def foo():
    time.sleep(2)
    print("function foo running")

def timer(func):
    start_time = time.time()
    func()
    stop_time = time.time()

    print("running time %s" % (start_time - stop_time))

    return func

foo = timer(foo)

foo()

>>function foo running
running time -2.0002999305725098
function foo running    

# 以foo()的形式运行函数foo,实现了不改变函数调用方式运行函数
# 但运行过程中多执行了一次foo,
# 第一次执行是timer函数中的func()
# 第二次执行是timer函数返回了foo函数名,最后运行foo()再次运行了foo原函数

根据以上示例可以得出单纯使用高阶函数无法完全实现在不改变函数调用方式的基础下增加函数的功能

2. 函数嵌套

定义:在函数里面嵌套定义函数

def country(name):
    print('country is %s' %name)
    def province():
        print('guangdong')
        def city():
            print('shenzhen')
        city()
    province()

country('china')

>>country is china
guangdong
shenzhen

3. 闭包

定义:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)

def country(name):
    print('country is %s' %name)
    def province():
        print('province is %s' % name)
        def city():
            print('city is %s' % name)
        city()
    province()

country('china')

province()函数就是一个闭包

4. 使用高阶函数+函数嵌套+闭包实现为函数增加功能

import time

def timer(func):
    def inner():
        start_time = time.time()
        func()
        stop_time = time.time()
        print("running time is %s" % (stop_time- start_time))
    return inner

def foo():
    print("foo function")
    time.sleep(2)

f = timer(foo) # f就是timer函数的返回值--inner函数的函数指针

f() # f加括号执行f函数

>>foo function
running time is 2.000217914581299

上述示例实现了为foo函数增加函数运行时间计时的功能,如果将f改为foo,则实现了不改变函数的调用方式同时增加了函数的功能,如下所示

import time

def timer(func):
    def inner():
        start_time = time.time()
        func()
        stop_time = time.time()
        print("running time is %s" % (stop_time- start_time))
        return inner

def foo():
    print("foo function")
    time.sleep(2)

foo = timer(foo) 

foo() 

>> foo function
running time is 2.000906229019165
# 最后执行的foo函数相比于最初定义的foo函数,实现了增加函数运行时间计时的功能

到此基本实现了在不改变函数的调用方式以及函数源码的前提下,实现了增加函数功能,但还需要做一步函数赋值的操作,如果其他函数需要怎加同样的功能,还要对其他函数做赋值操作,这是很麻烦的。

使用装饰器可以避免这样的麻烦,只要在添加功能的目标函数前添加一行代码:@功能函数。实现方法如下所示

import time

def timer(func):
    def inner():
        start_time = time.time()
        func()
        stop_time = time.time()
        print("running time is %s" % (stop_time- start_time))
    return inner

@timer
def foo():
    print("foo function")
    time.sleep(2)

foo()

>>foo function
running time is 2.000242233276367

5. 获取被装饰函数的返回值

import time

def timer(func):
    def inner():
        start_time = time.time()
        ret = func() # 执行func函数得到函数返回值,赋值给ret
        stop_time = time.time()
        print("running time is %s" % (stop_time- start_time))
        return ret # inner的返回值为ret
    return inner # timer的返回值为inner函数名

@timer
def foo():
    print("foo function")
    time.sleep(2)
    return "return value foo"

ret = foo()
# 首先假设foo = A
# 执行foo() <==> 首先执行A = timer(foo),然后执行A()
# timer(foo)的返回值为inner,即A = inner
# 执行A() <==> 执行inner()
# 执行inner()过程中执行inner函数体中的所有代码,得到最初定义的foo函数的返回值“return value foo”
# 即执行A()得到返回值“return value foo”
# ret = A() 将A()的返回值赋值给ret, 即使用ret=foo()可以得到最初定义的foo函数的返回值
print(ret)

>>foo function
running time is 2.0001463890075684
return value foo

6. 被修饰函数有参数

import time

def timer(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        ret = func(*args, **kwargs) # 执行func函数得到函数返回值,赋值给ret
        stop_time = time.time()
        print("running time is %s" % (stop_time- start_time))
        return ret # inner的返回值为ret
    return inner # timer的返回值为inner函数名

@timer
def foo(name):
    print("usr name is %s" % name)
    time.sleep(2)
    return "return value foo"

ret = foo("Jack")
# 首先假设foo = A
# 执行foo() <==> 首先执行A = timer(foo),然后执行A()
# timer(foo)的返回值为inner,即A = inner
# 执行A() <==> 执行inner()
# 执行inner()过程中执行inner函数体中的所有代码,
# 执行到func函数时,即执行被修饰函数foo(name),foo有参数,则func也需要参数
# func的参数需要从A()的参数中获得,即inner的参数就是func的参数
# 由于参数是不定的,可以为函数添加参数 *args, **kwargs

print(ret)

>> usr name is Jack
running time is 2.0003693103790283
return value foo

7. 带参数装饰器

import time

def type_check(*args, **kwargs):
    file_type = kwargs["file_type"]
    print("file type %s" %file_type)

    def timer(func):
        def inner(*args, **kwargs):
            start_time = time.time()
            ret = func(*args, **kwargs) # 执行func函数得到函数返回值,赋值给ret
            stop_time = time.time()
            print("running time is %s" % (stop_time- start_time))
            return ret # inner的返回值为ret
        return inner # timer的返回值为inner函数名

    return timer

@type_check(file_type = "mysql")
def foo(name):
    print("usr name is %s" % name)
    time.sleep(2)
    return "return value foo"

ret = foo("Jack")
# type_check加括号首先执行type_check(),获取type_check的参数
# foo <==> A = type_check(), A得到timer返回值
# 执行foo() <==> timer()
# 相比于无参装饰器,有参装饰器在无参装饰器的基础上套一层函数来传参

print(ret)

>>file type mysql
usr name is Jack
running time is 2.00007963180542
return value foo

8. 多个装饰器

def outer_0(func):
    def inner(*args, **kwargs):
        print('3.5')
        ret = func(*args, **kwargs)
        return ret
    return inner

def outer(func):
    def inner(*args, **kwargs):
        print('123')
        ret = func(*args, **kwargs)
        print('456')
        return ret
    return inner

@outer_0
@outer
def index(a1, a2):
    print('complex')
    return a1 + a2

print(index(1, 2))


>>3.5
>>123
>>complex
>>456
>>3
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值