装饰器的理解与案例详解

一. 装饰器函数的构成

假设业务部门需要对四个不同功能的函数,增加验证功能,如何对下列代码进行修改?

def foo1():
    print("foo1")


def foo2():
    print("foo2")


def foo3():
    print("foo3")


def foo4():
    print("foo4")


foo1()
foo2()
foo3()
foo4()

需要验证功能,可以写一个验证功能的函数,插入到每一个功能函数中

# -*- coding:utf-8 -*-
def check_foo():
    print("check")
    
    
def foo1():
	check_foo()
    print("foo1")


def foo2():
	check_foo()
    print("foo2")


def foo3():
	check_foo()
    print("foo3")


def foo4():
	check_foo()
    print("foo4")


foo1()
foo2()
foo3()
foo4()

但是,需要了解的是,公司中,老板一般会要求:对代码的修改遵循开放封闭原则,开放-可对代码进行扩展,封闭-已经实现的代码不允许修改。

但是,每个功能代码中插入了自己的实现函数,已经进行了修改,没有满足老板的要求。

这时就需要使用python中的装饰器,它的组成类似于闭包。

扩展自己写的check_foo函数,封装成一个装饰器函数

def wrapper(foo):
	def check_foo():
		print("check")
		foo()
	return check_foo

随后使用@[函数名]在每一个功能函数上来调用装饰器函数

# -*- coding:utf-8 -*-
@wrapper
def foo1():
    print("foo1")


@wrapper
def foo2():
    print("foo2")


@wrapper
def foo3():
    print("foo3")


@wrapper
def foo4():
    print("foo4")


foo1()
foo2()
foo3()
foo4()

执行结果:

check
foo1
check
foo2
check
foo3
check
foo4

二. 装饰器函数的过程

  • 在使用@wrapper时,首先执行wrapper函数,且将@wrapper下的函数的引用作为参数传递给wrapper函数去执行
  • 后续过程与闭包函数的执行过程一样

闭包函数的执行过程

@wrapper可以理解为,等于wrapper(foo1)

三. 装饰器的深入使用

1. 装饰器嵌套执行

例子:

# -*- coding:utf-8 -*-
def font1(fn):
    def wrapper():
        return "<b>" + fn() + "</b>"
    return wrapper


def font2(fn):
    def wrapper():
        return "<i>" + fn() + "</i>"
    return wrapper


@font1
def words1():
    return "today is a good day!"


@font2
def words2():
    return "nothing can change my mind"

@font1
@font2
def words3():
    return "don't worry, be happy!"


print(words1())
print(words2())

# 将装饰器font2的结果给装饰器font1,装饰器嵌套执行
print(words3())

结果:

<b>today is a good day!</b>
<i>nothing can change my mind</i>
<b><i>don't worry, be happy!</i></b>

从最靠近函数的装饰器先执行,将执行完成的结果封装成一个函数,该函数再向外执行一个装饰器,迭代操作,直到完结。

2. 被装饰的函数带参数

被装饰的函数的参数会传入到,内嵌函数中当参数使用
例子:

# -*- coding:utf-8 -*-
from time import ctime, sleep


def time_foo(foo):
    def wrapped_func(a, b):
        print("%s 被调用 %s" % (foo.__name__, ctime()))
        print(a, b)
        foo(a, b)
    return wrapped_func


@time_foo
def fun(a, b):
    print(a + b)


fun(1, 3)
sleep(2)
fun(5, 7)

结果:

fun 被调用 Mon Dec 23 16:38:58 2019
1 3
4
fun 被调用 Mon Dec 23 16:39:00 2019
5 7
12

3. 被装饰函数的return返回值

接上面的例子,去掉被装饰函数以及装饰器内的参数,再在程序末尾加上一个speak函数,且不同于fun函数,函数内部是加上return返回值而不是print,但最后的结果不显示其返回值。
例子:

# -*- coding:utf-8 -*-
from time import ctime, sleep


def time_foo(foo):
    def wrapped_func():
        print("%s 被调用 %s" % (foo.__name__, ctime()))
        foo()
    return wrapped_func


@time_foo
def fun():
    print("hello")


@time_foo
def speak():
    return "prefect!"

fun()
speak()
print(speak())

结果:

fun 被调用 Mon Dec 23 16:55:32 2019
hello
speak 被调用 Mon Dec 23 16:55:32 2019
speak 被调用 Mon Dec 23 16:55:32 2019
None

原因是,此处的speak()由于被装饰器装饰,其返回值为装饰器内部嵌套函数wrapped_func的返回值,由于内部嵌套函数无返回值,所以打印为空None。

所以,要打印出被嵌套函数返回值,即,使装饰器内嵌套函数返回值变为被嵌套函数返回值。
即,修改

foo()return foo()

4. 装饰器带参数

规律:从内到外,第一层函数,参数为被修饰函数参数;第二层函数,参数为被修饰函数的引用;第三层函数,参数为装饰器参数

# -*- coding:utf-8 -*-
from time import ctime


def time_foo_param(fal="prefect"):
    def time_inner(foo):
        def time_wrapper():
            print("%s called at %s %s" % (foo.__name__, ctime(), fal))
            return foo()
        return time_wrapper
    return time_inner


@time_foo_param()
def func():
    print('Hello Python!')

func()

这里foo() == time_foo_param()(foo)()

至于前面的双层嵌套函数,规律同上面总结的一样,只不过没有第三层函数,所以无法为装饰器添加参数,但前两层所具备的功能都包含。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值