python functools update_wrapper,wraps使用

  • update_wrapper 使用

此函數主要用在裝飾器函數中,裝飾器返回函數反射得到的是包裝函數的函數定義而不是原始函數定義

如下代碼 整合 update_wrapper源碼 和 示例代碼在一個文件中,方便查看;

# -*- coding: utf-8 -*-
"""
(C) Guangcai Ren <[email protected]>
All rights reserved
create time '2020/3/23 17:02'

Module usage:
functools.update_wrapper 使用方法
主要思路 是 被調用函數屬性賦值給 內部函數
主要技術 setattr,getattr
"""

WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
                       '__annotations__')
WRAPPER_UPDATES = ('__dict__',)


def update_wrapper(wrapper,
                   wrapped,
                   assigned=WRAPPER_ASSIGNMENTS,
                   updated=WRAPPER_UPDATES):
    """Update a wrapper function to look like the wrapped function
        主要 把 wrapped 的 相關屬性(WRAPPER_ASSIGNMENTS) 賦值給 wrapper 函數,並返回 wrapper 函數
       wrapper is the function to be updated:此參數爲被賦值的函數, 返回值也是此參數
       wrapped is the original function:被裝飾的函數
       assigned is a tuple naming the attributes assigned directly:被賦值的相關屬性,默認爲 WRAPPER_ASSIGNMENTS
       from the wrapped function to the wrapper function (defaults to
       functools.WRAPPER_ASSIGNMENTS)
       updated is a tuple naming the attributes of the wrapper that
       are updated with the corresponding attribute from the wrapped
       function (defaults to functools.WRAPPER_UPDATES)
    """
    for attr in assigned:
        try:
            value = getattr(wrapped, attr)
        except AttributeError:
            pass
        else:
            # 把 wrapped 的 相關屬性(默認爲WRAPPER_ASSIGNMENTS) 賦值給 wrapper 函數
            setattr(wrapper, attr, value)
    for attr in updated:
        getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
    # Issue #17482: set __wrapped__ last so we don't inadvertently copy it
    # from the wrapped function when updating __dict__
    wrapper.__wrapped__ = wrapped
    # Return the wrapper so this can be used as a decorator via partial()
    # 返回 帶有 wrapped 的 相關屬性 的 新 wrapper
    return wrapper


def wrap_fun(fun):
    def call_fun(*args, **kwargs):
        """
        內部調用函數,每次調用都不是被調用函數相關屬性
        :param args:
        :param kwargs:
        :return:
        """
        print('print call fun')
        return fun(*args, **kwargs)

    # 此處直接返回 call_fun 的相關屬性,導致 normal_fun.__name__獲取的是 call_fun的相關屬性
    return call_fun


@wrap_fun
def normal_fun():
    """
    一個普通函數
    :return:
    """
    print('print normal function')


def wrap_fun2(fun):
    """

    :param fun:
    :return:
    """

    def call_fun2(*args, **kwargs):
        """
        內部調用函數2
        :param args:
        :param kwargs:
        :return:
        """
        print('print call fun2')
        return fun(*args, **kwargs)

    # 把 被裝飾的函數 fun=normal_fun2 的相關屬性 賦值給 call_fun2函數,並且 wrap_fun2 返回值爲 call_fun2,這樣
    # 被裝飾函數normal_fun2.__name__便是自己的名字

    return update_wrapper(call_fun2, fun)


@wrap_fun2
def normal_fun2():
    """
    成功進行屬性賦值的函數
    :return:
    """
    print('print normal function2')


if __name__ == '__main__':
    print('未進行屬性賦值情況')
    normal_fun()
    print(normal_fun.__name__)
    print(normal_fun.__doc__)

    print('進行屬性賦值')
    normal_fun2()
    print(normal_fun2.__name__)
    print(normal_fun2.__doc__)

運行結果爲:

  • wraps 使用

主要是對 上述 update_wrapper方法的封裝,在開發中更加方便

源碼爲:


def wraps(wrapped,
          assigned = WRAPPER_ASSIGNMENTS,
          updated = WRAPPER_UPDATES):
    """Decorator factory to apply update_wrapper() to a wrapper function

       Returns a decorator that invokes update_wrapper() with the decorated
       function as the wrapper argument and the arguments to wraps() as the
       remaining arguments. Default arguments are as for update_wrapper().
       This is a convenience function to simplify applying partial() to
       update_wrapper().
    """
    return partial(update_wrapper, wrapped=wrapped,
                   assigned=assigned, updated=updated)

所以@wraps的等價形式如下:
wrapper = partial(update_wrapper, wrapped=func, assigned=assigned, updated=updated)(wrapper)
進一步等價:
wrapper = update_wrapper(wrapper=wrapper, wrapped=func, assigned=assigned, updated=updated)

關於 partial 的理解如下鏈接:xxx

示例代碼:


from functools import wraps


def wrap_fun3(fun):
    """

    :param fun:
    :return:
    """

    @wraps(fun)
    def call_fun3(*args, **kwargs):
        """
        內部調用函數3
        :param args:
        :param kwargs:
        :return:
        """
        print('print call fun3')
        return fun(*args, **kwargs)

    return call_fun3


@wrap_fun3
def normal_fun3():
    """
    成功使用wrpas進行屬性賦值的函數
    :return:
    """
    print('print normal function3')


if __name__ == '__main__':
    print('使用wraps進行屬性賦值')
    normal_fun3()
    print(normal_fun3.__name__)
    print(normal_fun3.__doc__)

結果如下:

 

相關鏈接:

http://blog.chinaunix.net/uid-8599612-id-3012687.html

https://www.sohu.com/a/331923444_571478

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章