美文网首页程序员
Python干货-装饰器与偏函数

Python干货-装饰器与偏函数

作者: 东南有大树 | 来源:发表于2018-10-30 16:02 被阅读20次

    装饰器

    装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

    概括的讲,装饰器的作用就是为已经存在的函数或对象添加额外的功能。

    举个简单的栗子:

    # 定义一个记录日志用来调试的函数
    def debug(func):
        def wrapper():
            print("[DEBUG]: enter {}()".format(func.__name__))
            return func()
        return wrapper
    
    # 定义一个say_hello函数
    def say_hello():
        print("hello!")
    
    # 调用debug函数并将say_hello函数当作参数传入,返回的是wrapper函数
    hello = debug(say_hello)
    # 执行wrapper函数
    hello()
    
    [DEBUG]: enter say_hello()
    hello!
    

    debug函数对say_hello函数进行了包装,也就是增加了更多功能,实际上,debug函数已经是一个装饰器了

    更加简单的写法是使用语法糖语法@debug,示例如下:

    # 定义一个记录日志用来调试的函数
    def debug(func):
        def wrapper():
            print("[DEBUG]: enter {}()".format(func.__name__))
            return func()
        return wrapper
    
    @debug
    # 定义一个say_hello函数
    def say_hello():
        print("hello!")
    
    say_hello()
    
    [DEBUG]: enter say_hello()
    hello!
    

    @debug的效果与hello = debug(say_hello)是一样的,是不是很惊艳!

    关于参数问题

    定装饰器函数wrapper中调用了原函数say_hello,但如果原函数有参数,或者有很多参数的时候,可以利用python多参数形式,如下:

    # 定义一个记录日志用来调试的函数
    def debug(func):
        def wrapper(*args, **kwargs):
            print("[DEBUG]: enter {}()".format(func.__name__))
            return func(*args, **kwargs)
        return wrapper
    
    @debug
    # 定义一个say_hello函数
    def say_hello(name, world):
        print(world, name)
    
    say_hello('Jack', 'hello')
    
    [DEBUG]: enter say_hello()
    hello Jack
    

    通过使用*args, **kwargs这得传参数形式,支持元组形式或字典形式的传值结构,不管是有参数还是多个参数,都可以很好的应对

    带参数的装饰器

    定义一个可以指定日志输出级别的“日志输出装饰器函数”

    def logging(level):
        def wrapper(func):
            def inner_wrapper(*args, **kwargs):
                print("[{level}]: enter function {func}()".format(
                    level=level,
                    func=func.__name__))
                return func(*args, **kwargs)
            return inner_wrapper
        return wrapper
    
    @logging(level='INFO')
    def say(something):
        print("say {}!".format(something))
    
    # 如果没有使用@语法,等同于
    # say = logging(level='INFO')(say)
    
    @logging(level='DEBUG')
    def do(something):
        print("do {}...".format(something))
    
    say('hello')
    do("my work")
    
    [INFO]: enter function say()
    say hello!
    [DEBUG]: enter function do()
    do my work...
    

    解析:

    当带参数的装饰器被打在某个函数上时,比如@logging(level='DEBUG'),它其实是一个函数,会马上被执行

    而被执行的logging函数返回的结果,才是一个装饰器函数O(∩_∩)O哈哈~

    偏函数

    偏函数可以设定函数的默认行为,示例如下:

    num = int('12345')
    print(num)
    
    12345
    

    在默认情况下,int()函数会将字符串数字转换成十进制的数据,便也可以通过设定参数来让int()函数将其转换成八进制数字,如下:

    num = int('12345', base=8)
    print(num)
    
    5349
    

    那如何改变int()函数的默认行为呢?

    # 首先导入 functools 模块
    import functools
    
    # 定义一个默认转换为二进制的int函数
    int2 = functools.partial(int, base=2)
    # 调用 
    int2('10110101')
    
    181
    

    functools.partial的作用就是将固定一个函数的默认行为,从而简化之后的使用

    来看看关于它的描述:

    Init signature: functools.partial(self, /, *args, **kwargs)
    Docstring:     
    partial(func, *args, **keywords) - new function with partial application
    of the given arguments and keywords.
    

    所以,这个方法可以接收函数、*args、**kwargs这些对象

    这里的base=2是字典结构的参数

    max2 = functools.partial(max, 10)
    max2(5, 6, 7)  # 相当于max2(10, 5, 6, 7)
    
    10
    

    小结
    当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。

    相关文章

      网友评论

        本文标题:Python干货-装饰器与偏函数

        本文链接:https://www.haomeiwen.com/subject/ybbgtqtx.html