Python 装饰器-原理解析(进阶)


介绍装饰器装饰函数如何使用,会 源源不断更新。

python装饰器简介

装饰器是python中非常重要的一个部分,顾名思义就是可以对函数的功能进行装饰扩展。如果有以下需求,可以考虑使用装饰器。

  • 对多个函数进行相同功能的扩展
  • 方便后期维护
  • 遵循开闭原则(OCP)

开闭原则:程序设计,要求开放对程序的扩展,关闭对程序的修改。

注: 引入装饰器前,我们需要知道python可以在函数内定义函数,函数的返回值也可以是函数。
   函数名func不加小括号()是对函数的调用,不执行;
   函数名加小括号func()表示执行函数,

装饰器装饰(无参函数)

普通函数

def func1():
    print("零否!")

def func2():
    print("源源不断!")
    
func1()
print("------")
func2()

output:

零否!
------
源源不断!

函数扩展1(修改原函数)

对函数func1func2进行扩展,如下:

def func1():	#新函数
    print("Hello!")
    print("零否!")
    print("Bye!")


def func2():	#新函数
    print("Hello!")
    print("源源不断!")
    print("Bye!")


func1()
print("------")
func2()

output:

Hello!
零否!
Bye!
------
Hello!
源源不断!
Bye!

此方法虽然实现了对功能的扩展,如果函数过多时,会非常麻烦,不仅不方便后期维护,且破坏了开闭原则。

函数扩展2(新函数去调用旧函数)

对函数扩展,还可创建新函数去调用旧函数,如下

def func1():
    print("零否!")

def new_func():		#新函数
    print("Hello!")
    func1()
    print("Bye!")

new_func()

output:

Hello!
零否!
Bye!

此处创建了一个新函数new_func(),在new_func()中调用了函数func1(),实现了对函数的扩展,满足了开闭原则,但是对多个函数进行扩展时,依旧麻烦,且不易维护!

函数扩展3(旧函数当作新函数的参数)

创建新函数,把旧函数当作新函数的参数传入,如下

def func1():
    print("零否!")

def func2():
    print("源源不断!")


def new_func(old_func):		#新函数
    print("Hello!")
    old_func()
    print("Bye!")

old_func=func1
new_func(old_func)
print("------")
old_func=func2
new_func(old_func)

output:

Hello!
零否!
Bye!
------
Hello!
源源不断!
Bye!

此方法和上述方法类似,把函数当作参数(func,函数名不加())传入,此方法已经接近装饰器的使用。如果传入的函数有参数,此方法不适用,后文会介绍到装饰器装饰(有参函数)

函数扩展4(引入装饰器)

文章开篇已经提到python可以在函数内定义函数,函数的返回值也可以是函数。

def func1():
    print("零否!")

def func2():
    print("源源不断!")

    
def decorate_func(old_func):		#装饰器
    def new_func():			#新函数
        print("Hello!")
        old_func()
        print("Bye!")

    return new_func


old_func = func1		#调用函数func1
f1 = decorate_func(old_func)
f1()
'''
等同于
decorate_func(func1)()
'''
print("------")
old_func = func2		#调用函数func2
f2 = decorate_func(old_func)
f2()
'''
等同于
decorate_func(func2)()
'''

output:

Hello!
零否!
Bye!
------
Hello!
源源不断!
Bye!

此处使用了装饰器,但是不是装饰器经典用法。

函数扩展5(装饰器经典用法)

@装饰器名 是装饰器的经典用法
以下两种装饰器写法不同,效果相同
写法一,装饰没有返回值的函数
写法二,装饰有返回值的函数,函数没有返回值也可以使用该写法。(经典写法)

#以下两种装饰器写法不同,效果相同
# 写法一,适合没有返回值的函数
def decorate_func(old_func):	#装饰器	
    def new_func():		#新函数
        print("Hello!")
        old_func()
        print("Bye!")

    return new_func
    
# 写法二,适合有返回值的函数
# 函数没有返回值也可以使用该写法。
def decorate_func(old_func):	#该写法更规范
    def new_func():		#新函数
        print("Hello!")
        result = old_func()
        print("Bye!")
        return result

    return new_func
    
@decorate_func		#经典用法 @装饰器名
def func1():
    print("零否!")

@decorate_func		#经典用法 @装饰器名
def func2():
    print("源源不断!")

func1()
print("------")
func2()

'''
等同于
def func1():
    print("零否!")

decorate_func(func1)()
'''

output:

Hello!
零否!
Bye!
------
Hello!
源源不断!
Bye!

装饰器装饰(有参函数)

上面介绍了无参装饰器的用法,下面直接介绍有参函数装饰器的用法。

普通函数

def func3(a, b):
    sum = a + b
    return sum

def func4(c, d):
    mul = c * d
    return mul

output:

8
------
18

装饰器1(已知参数列表)

对已知函数参数列表的函数,进行装饰器扩展,如下

def decorate_func(old_func):
    def new_func(a,b):
        print("开始计算:")
        result = old_func(a,b)	#函数执行后返回结果
        print("计算结束。")
        return result

    return new_func

@decorate_func
def func3(a, b):
    sum = a + b
    return sum

f3 = func3(2,6)
'''
#等同于
def func3(a, b):
    sum = a + b
    return sum
    
decorate_func(func3)(2, 6)
'''

@decorate_func
def func4(a, b):
    mul = a * b
    return mul


f4 = func4(3,6)
print(f3)
print("------")
print(f4)

output:

开始计算:
计算结束。
8
------
开始计算:
计算结束。
18

装饰器2(任意参数列表)

我们知道有些函数有参数,有些函数没有参数,因此我们需要一个,可以装饰任意函数(不管有无参数)的装饰器。

python中有可以接收任意参数的关键字
*args接收元组作为位置参数(任意个)
*kwargs 接收任意个关键字参数(即任意个字典参数)

def decorate_func(old_func):
    def new_func(*args, **kwargs):	#对参数列表进行装包
        print("开始计算:")
        result = old_func(*args, **kwargs)	#对参数列表进行拆包
        print("计算结束。")
        return result

    return new_func


@decorate_func
def func2():
    print("源源不断!")


@decorate_func
def func3(a, b):
    sum = a + b
    return sum


func2()
print('------')
f3 = func3(2, 7)
print(f3)

output:

开始计算:
源源不断!
计算结束。
------
开始计算:
计算结束。
9

多装饰器

一个函数指定多个装饰器

def decorate_func1(old_func):
    def new_func(*args, **kwargs):
        print("装饰器1--hello")
        result = old_func(*args, **kwargs)
        print("装饰器1--bye!")
        return result

    return new_func


def decorate_func2(old_func):
    def new_func(*args, **kwargs):
        print("装饰器2--hello")
        result = old_func(*args, **kwargs)
        print("装饰器2--bye!")
        return result

    return new_func

@decorate_func1
@decorate_func2
def func1():
    print("零否!")


@decorate_func2
@decorate_func1
def func2():
    print("源源不断!")


func1()
'''
#等同于
def func1():
    print("零否!")

decorate_func1(decorate_func2(func1))()
'''
print('------------')
func2()

output:

装饰器1--hello
装饰器2--hello
零否!
装饰器2--bye!
装饰器1--bye!
------------
装饰器2--hello
装饰器1--hello
源源不断!
装饰器1--bye!
装饰器2--bye!

多装饰器解释(由内而外)

从结果可以可以看出,装饰器装饰函数是由内而外的,执行顺序是由外而内的。

@decorate_func2 装饰 func1

@decorate_func2
def func1():
    print("零否!")

func1()

'''
#等同于
def func1():
    print("零否!")
    
decorate_func2(func1)()
'''

output:

装饰器2--hello
零否!
装饰器2--bye!

@decorate_func1 装饰 ( @decorate_func2 装饰 func1的结果)

@decorate_func1
@decorate_func2
def func1():
    print("零否!")

func1()

'''
#等同于
def func1():
    print("零否!")

decorate_func1(decorate_func2(func1))()
'''

output:

装饰器1--hello
装饰器2--hello
零否!
装饰器2--bye!
装饰器1--bye!

(有参装饰器)装饰函数

给装饰器传入参数

def decorate_param(param):  # 装饰器传入参数
    def decorate_func(old_func):  # 装饰器传入函数
        def new_func(*args, **kwargs):
            print("hello")
            print(f"装饰器参数:{param}")  # 装饰器参数param
            result = old_func(*args, **kwargs)
            print("bye!")
            return result

        return new_func

    return decorate_func


@decorate_param("零否")
def func(a, b):
    sum = a + b
    print(f'{a} + {b} = {sum}')
    return sum


func(1, 2)
'''
#等同于
def func(a, b):
    sum = a + b
    print(f'{a} + {b} = {sum}')
    return sum

decorate_param("零否")(func)(1,2)
'''

output:

hello
装饰器参数:零否
1 + 2 = 3
bye!

文章中如有发现问题或错误,可以留言指正,谢谢!

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值