zoukankan      html  css  js  c++  java
  • metadata流失与带参装饰器

    2个问题

    • 如何解决被装饰过的函数的metadata的流失的问题?
    • 带有参数的装饰器有什么用处?

    如何解决被装饰过的函数的metadata的流失的问题?

    # decorator
    def log_func(func):
        def wrapper(*args, **kwargs):
            print('Function name: {}'format(func.__name__))
            return func(*args, **kwargs)
        return wrapper
    
    # decorated function
    @log_func
    def multiply(x, y):
        'Multiply x and y'
        return x * y
    
    multiply(1, 3)
    # >> Function name: multiply
    # >> 3

    似乎看起来没问题,但是如果我们进一步检查被装饰过后的函数multiply各种attributes,我们就会发现问题:

    # original function
    def multiply(x, y):
        'Multiply x and y'
        return x * y
    
    multiply.__name__
    # >> 'multiply'
    multiply.__doc__
    # >> 'Multiply x and y'
    
    
    # decorated function
    @log_func
    def multiply(x, y):
        'Multiply x and y'
        return x * y
    
    multiply.__name__
    # >> 'wrapper'
    multiply.__doc__
    # >> 

    可以看到__doc___name__等函数的metadata,都在被装饰后,都丢失了。为了解决这个问题,我们可以在装饰器里,把这些metadata都复制到被装饰过后到函数里。

    # original decorator
    def log_func(func):
        def wrapper(*args, **kwargs):
            print('Function name: {}'format(func.__name__))
            return func(*args, **kwargs)
        return wrapper
    
    
    # decorator that copies metatdata
    def log_func(func):
        def wrapper(*args, **kwargs):
            print('Function name: {}'format(func.__name__))
            return func(*args, **kwargs)
        wrapper.__name__ = func.__name__ # copy name
        wrapper.__doc__ = func.__doc__ # copy doc
        return wrapper

    在python的functools包里有提供一个装饰器@wraps就专门提供这个功能,所以我们的log_func装饰器可以重新定义为

     # decorator that copies metatdata with @functools.wraps
    import functools
    
    def log_func(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print('Function name: {}'format(func.__name__))
            return func(*args, **kwargs)
        return wrapper
    
    
    # decorate function with the above decorator
    @log_func
    def multiply(x, y):
        'Multiply x and y'
        return x * y
    
    multiply.__name__
    # >> 'multiply'
    multiply.__doc__
    # >> 'Multiply x and y'

    带有参数的装饰器有什么用处?

    什么是带参数的装饰器? 细心的朋友可能已经发现在上文里,我介绍的@functools.wraps在@语法中,有一个参数func@functools.wraps(func)。而常见的装饰器在@语法中是没有参数的,比如上文用到的@log_func 。类似@functools.wraps(func)就是带参数的装饰器;装饰器可以带多个参数@decorator(x, y)

    看一个例子。

    @log_msg_func("Function name: {name}")
    def multiply(x, y):
        return x * y

    在这个例子里,log_msg_func就是一个带参数的decorator。其实就等同于

    log_func = log_msg_func("Function name: {name}")
    
    @log_func
    def multiply(x, y):
        return x * y

    进一步等同于

    def multiply(x, y):
        return x * y
    multiply = log_msg_func("Function name: {name}")(multiply)

    可见带参数的装饰器就一个返回值为一个装饰器的函数,其实就是一个“装饰器生成器“。这个例子里,log_msg_func的参数就是一个string,返回值就是一个装饰器。 我们看看其是怎么定义的:

    def log_msg_func(msg):
        def log_func(func):
            def wrapper(*args, **kwargs):
                print(msg.format(name=func.__name__))
                return func(*args, **kwargs)
            return wrapper
        return log_func

    从上面的定义里面可以看出,msg这个string参数为要生成的装饰器log_func提供了一个enclosing环境。有了这个log_msg_func”装饰器生成器“ ,我们可以轻松改变log里信息,进一步避免重复的代码。这就是带参数的装饰器的用处!

  • 相关阅读:
    jquery文本折叠
    物理小词典
    程序员的十层楼
    各种语言的hello world
    读书遇到的一些概念
    银行业务一些概念
    mysql 基本操作
    oracle 基本操作
    maven 基本操作
    ubuntu JavaWeb环境搭建
  • 原文地址:https://www.cnblogs.com/soymilk2019/p/15035975.html
Copyright © 2011-2022 走看看