返回顶部

浅析Python装饰器

一、装饰器实现

比如程序中有个原本的功能函数,打印func,返回value

def func():
    print("func")
    value = (1, 2, 3, 4)
    return value

新需求:要将在输出fun之前,打印before,之后打印after

1、方式一

方式一:修改功能函数内部代码实现

def func():
    # 新增功能
    print("before")
    
    print("func")
    value = (1, 2, 3, 4)
    # 新增功能
    print("after")
    return value

2、方式二

方式一很容易实现。如果使用装饰器,如方式二。
方式二:装饰器实现功能

def func():    
    print("func")
    value = (1, 2, 3, 4)
    return value

def outer(origin):
    def inner():
        # 新增功能
        print("before")
        res = origin()
        # 新增功能
        print("after")
        return res
    return inner

func = outer(func)
res = func()

在装饰器实现方式中,将func函数对象作为参数传入outer函数,然后inner函数中调用原func函数之外增加额外需求的功能。func=outer(func)

二、装饰器改进

上述方式二中func = outer(func)用来表示用outer修饰func,python提供一种更加灵活的装饰方式。
利用python支持的特殊语法

@decorator
def func():    
    pass
    
func()

内部会自动执行 func=decorator(func)
也就是调用func相当于调用decorator(func)

1、方式三

根据语法修改上述方式二装饰器变为方式三:

def outer(origin):    
    def inner():        
        print("before")        
        res = origin()        
        print("after")        
        return res    
    return inner
    
# 注意装饰器与被装饰函数代码中的顺序
@outer
def func():       
    print("func")    
    value = [1, 2, 3]    
    return  value

res = func()

可以看到方式一实现简单,要修改源代码,不符合代码习惯。方式二实现复杂,可读性差了一些,但是方式二的优势体现在扩展性更高,如下。

def outer(origin):    
    def inner():        
        print("before")        
        res = origin()        
        print("after")        
        return res    
    return inner

@outer
def func1():       
    print("func1")    
    value = [1, 2, 3]    
    return  value

@outer
def func2():
    print("func2")
    value = [1, 2, 3]
    return  value

@outer
def func3():
    print("func3")
    value = [1, 2, 3]
    return  value

res1 = func1()
res2 = func2()
res3 = func3()

如上,当有多个需要被装饰的函数时候,可以直接调用装饰器装饰,而不需要像方式一一样逐个修改功能函数,不容易出错,更加灵活,代码更加健壮。

2、方式四

但是方式三的问题在于,如果加入各个func函数都需要传参,并且参数类型,参数个数不同的时候,装饰器就无法统一接收,出现问题。因此可以使用可变参数优化函数器,使其支持接收任意函数参数。如方式四
方式四:优化装饰器

def outer(origin):
    def inner(*args, **kwargs):
        print("before")
        res = origin(*args, **kwargs)
        print("after")
        return res
    return inner

@outer
def func(a1):  # func = outer(func)
    print("func", a1)
    value = [1, 2, 3]
    return  value

@outer
def func2(a1, a2):
    print("func2", a1, a2)
    value = [1, 2, 3]
    return  value

@outer
def func3():
    print("func3")
    value = [1, 2, 3]
    return  value

res = func(12)
res2 = func2(11, a2=100)
res3 = func3()

func1, func2, func3参数个数不同,outer装饰器也能够正常接收,并传递给真正的功能函数func1, func2, func3

传参:

  • args, **kwargs用于编写可变长参数的函数,接收参数,args将接收的额外位置参数组成元组,**kwargs收集额外的关键字参数组成字典。
  • 实际起作用的是*与**,args和kwargs只是作为约定好的规范名称。

三、总结

1、decorator

Py中装饰器原理:基于@语法糖和函数闭包,将原函数封装在闭包中执行。
实现效果:可以在不改变函数内部代码以及调用方式的前提下,实现功能扩展
使用场景:多个函数系统统一执行前后需要自定义功能

示例

def outer(origin):
    def inner(*arg, **kwarg):
        # 执行前
        res = origin(*arg, **kwarg)
        # 执行后
        return res
    return inner

def func()
    pass
    
func()

2、场景

真正的业务场景中,如网站应用的很多操作,比如电商网站浏览商品页面添加购物车,博客页面点赞评论,这些操作都需要用户登录之后才能进行。真正的业务应该是用户将商品添加购物车,点赞评论,而额外的需求是要先判断是否登录,这部分额外的需求就用一个单独的装饰器来做。一旦涉及到判断用户登录状态的逻辑都可以调用这个装饰器。

Django伪码示例:

# 登录装饰器
def login(func):
    def login_fun(request, *args, **kwargs):
        """ 登录装饰器:如果用户已经登录,则正常执行,如果用户未登录,则跳转登录页面"""
        if 用户id in 登录用户列表:
            return func(request, *args, **kwargs)
        else:
            # 未登录重定向回登录页面
            red = HttpResponseRedirect(登录页面)
            return red
    return login_fun


@login
def order(request):
    """订单路由函数,执行具体查看订单并返回逻辑"""
    pass

如上,在处理订单之前先检测一下先用装饰器检查用户是否登录。如果用户已经登录则直接进入订单路由函数,如果没有登录,则返回登录页面先登录再查询用户的订单。

:以上仅为学习记录笔记,如果错误或者相同,轻喷,蟹蟹蟹蟹

posted on 2021-12-15 13:14  weilanhanf  阅读(51)  评论(0编辑  收藏  举报

导航