Python高级之装饰器

代码编写原则

  • 开放封闭原则
    • 封闭。不允许对已实现的功能修改
    • 开放。允许对已实现的功能进行拓展
  • 高内聚低耦合原则
    • 高内聚。针对的是模块内部,模块内的元素要密切一点,一般一个模块做一个功能
    • 低耦合。针对的是模块之间,模块之间的关联度要少一点

装饰器的定义

在不改变函数的定义和调用的前提下,给函数扩展功能,这就是装饰器

装饰器的格式

@装饰器函数
def 被装饰的函数

装饰器的本质

本质是闭包的外层函数调用了被装饰的的函数

闭包的概念及使用,见本人博客中的另一篇文章 Python高级之闭包

装饰器的实现逻辑

demo:

def w1(func):
    def inner():
        # 验证1
        # 验证2
        # 验证3
        func()
    return inner

@w1
def f1():
    print('f1')

python解释器就会从上到下解释代码,步骤如下:

  1. def w1(func): ==>将w1函数加载到内存
  2. @w1

从表面上看解释器仅仅会解释这两句代码,因为函数在 没有被调用之前其内部代码不会被执行.

但是

@w1内部会执行以下操作:

执行w1函数

执行w1函数 ,并将 @w1 下面的函数作为w1函数的参数,即:@w1 等价于 w1(f1) 所以,内部就会去执行:

def inner(): 
    #验证 1
    #验证 2
    #验证 3
    f1()    # func是参数,此时 func 等于 f1 
return inner# 返回的 inner,inner代表的是函数,非执行函数 ,其实就是将原来的 f1 函数塞进另外一个函数中
w1的返回值

将执行完的w1函数返回值 赋值 给@w1下面的函数的函数名f1 即将w1的返回值再重新赋值给 f1,即:

新f1 = def inner(): 
            #验证 1
            #验证 2
            #验证 3
            原来f1()

当调用f1()时,就回执行新的f1函数,在新f1函数内部先执行验证,再执行原来的f1函数,然后将原来f1 函数的返回值返回给了业务调用者。

好处:

在不改变原f1代码的情况下,即执行了验证的功能,又执行了原来f1函数的内容,还不改变原来f1函数的调用方式。

万能装饰器函数

demo:

def w1(func):
	def inner(*args, **kwargs):  # 接收被装饰函数传入参数
		print("装饰器内部接收参数:")
		print(args)
		print(kwargs)
		print("内部校验开始...")
		return func(*args, **kwargs)  # 调用func 需要传入参数
	return inner


@w1
def f1(*args, **kwargs):  # 传入参数
	print("函数传入的参数:")
	print(args)
	print(kwargs)
	print("业务函数开始执行...")
	return "嘿嘿嘿,我是返回值"


if __name__ == '__main__':
	print("------无参无返回值-------")
	f1()
	print("------有参无返回值-------")
	f1(111)
	print("------无参有返回值-------")
	print(f1())
	print("------有参有返回值-------")
	print(f1(222))

概要:

  • 内部函数需要有可变参数
  • 内部函数调用原函数指向,也要有可变参数
  • 被装饰的函数也要有可变参数
  • 满足以下四种情况:
    • 无参无返回值
    • 有参无返回值
    • 无参有返回值
    • 有参有返回值

多个装饰器装饰同一个函数

demo:

# 短信验证装饰器函数
def msg_veri(func):
	print("添加短信验证拓展功能")
	def inner1():
		print("正在执行短信验证拓展功能...")
		return func()  # 执行被调用函数的功能
	return inner1


# 密码验证装饰器函数
def pwd_veri(func):
	print("添加密码验证拓展功能")
	def inner2():
		print("正在执行密码验证拓展功能...")
		return func()  # 执行被调用函数的功能
	return inner2


@msg_veri
@pwd_veri
def f1():
	print("我是被装饰的函数...正在转账")

if __name__ == '__main__':
    # 执行转账业务
    f1()

调用顺序:

  • 添加装饰器功能 从下往上
  • 执行装饰器功能 从上往下

理解:

  1. 代码走到@msg_veri, 无法确定要装饰的函数,往下执行
  2. 代码走到@pwd_veri,确定要装饰的函数是f1,开始执行pwd_veri(f1),添加密码验证拓展功能,返回inner2给f1,此时f1=inner2
  3. 此时@msg_veri 确定要装饰的函数是f1,而f1指向inner2,开始执行msg_veri(inner2),添加短信验证拓展功能,返回的是inner1,并且赋值给f1,此时f1=inner1
  4. 代码走到f1(),调用f1,执行装饰器@msg_veri,执行短信验证拓展功能,然后调用inner2,执行密码验证拓展功能,最后调用原来f1内部的代码(转账功能)

带参数的装饰器装饰函数

需求: 在调用装饰器函数,希望额外传递一个参数 flag
flag == 1 : 开启身份验证拓展功能
flag == 0 : 不开启身份验证拓展功能

demo:

def outer(flag=0):  # 装饰器工厂,生产装饰器
	def w1(func):  # 真正的装饰器函数
		def inner():
			if flag == 1:
				print("正在进行身份校验...")
			return func()  # 调用原来的func(),将值返回
		return inner
	return w1


@outer(flag=1)
def f1():
	print("主业务函数执行中....")


if __name__ == '__main__':
	f1()

理解:

  1. 先执行@后面的outer(flag=1)函数,返回装饰器w1
  2. @后变成w1—>@w1,调用真正的装饰器,将f1传入w1中
  3. 返回inner赋值给f1
  4. 调用f1时,执行inner函数,先进行校验,再调用原来的主业务函数

类装饰器(几乎不用)

装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。在Python中一般callable对象都是函数,但也有例外。只要某个对象重写了 __call__() 方法,那么这个对象就是callable的。

class Test():
    def __call__(self):
        print('call me!')

t = Test()
t()  # call me

类装饰器demo:

class Person(object):
	def __init__(self, func):
		self.__func = func

	def __call__(self, *args, **kwargs):
		print("正在进行身份校验...")
		return self.__func()  # 执行被装饰的函数,并且返回


@Person  # 本质是Person(pay) 创建了一个Person的实例对象,并且赋值给了pay
def pay():
	print("主业务函数...支付功能")
	return "我是返回值"


if __name__ == '__main__':
	pay()  # pay=Person(pay)实例对象,pay() 实例对象后加()会自动调用类中的__call__方法

理解:

  1. 当用Person来装作装饰器对pay函数进行装饰的时候,首先会创建Person的实例对象pay=Person(pay),并且会把pay这个函数名当做参数传递到__init__方法中。即在__init__方法中的属性__func指向了pay指向的函数
  2. pay指向了Person创建出来的实例对象
  3. 当执行pay()时,相当于实例对象后加(),因此会自动调用类中的__call__方法
  4. 为了能够在__call__方法中调用原来pay指向的函数体,所以在__init__方法中就需要一个实例属性来保存这个函数体的引用
    所以才有了self.__func = func这句代码,从而在调用__call__方法中能够调用到pay之前的函数体

注意事项

使用函数装饰器会导致函数类型变成warpper类型, 所有的属性及内置函数都失效

def outer(func):
    """
    装饰器外部函数 outer
    """
    def inner():
        """
        装饰器内部函数 inner
        """
        return func()

    return inner


@outer
def f1():
    """
    被装饰的函数f1
    """
    pass


if __name__ == '__main__':
    print(f1.__name__)
    print(f1.__doc__)

结果

inner

        装饰器内部函数 inner

解决办法: 对装饰器内部函数加上 @wraps(func) 装饰

from functools import wraps


def outer(func):
    """
    装饰器外部函数 outer
    """
    @wraps(func)
    def inner():
        """
        装饰器内部函数 inner
        """
        return func()

    return inner


@outer
def f1():
    """
    被装饰的函数f1
    """
    pass


if __name__ == '__main__':
    print(f1.__name__)
    print(f1.__doc__)

结果

f1

    被装饰的函数f1
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值