• 浅析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
    

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

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

  • 相关阅读:
    zoj 1033 与其说是搜索,不如说是枚举
    hdu 4294 数学分析+搜索
    新的篇章
    Silverlight 利用DataGrid行加载事件动态控制行列显示
    (转)Excel中“不同的单元格格式太多”问题解决方法
    EasyUI tree的三种选中状态
    JS监听手机返回键
    Silverlight ComBox获取当前选中项的值
    DataGrdid 利用结果集反向转换成数据List
    silverlight 动态设置下拉框选中值
  • 原文地址:https://www.cnblogs.com/welan/p/15692236.html
Copyright © 2020-2023  润新知