What does functools.wraps do?

前端 未结 6 782
北海茫月
北海茫月 2020-11-22 06:23

In a comment on this answer to another question, someone said that they weren\'t sure what functools.wraps was doing. So, I\'m asking this question so that ther

6条回答
  •  北海茫月
    2020-11-22 06:59

    As of python 3.5+:

    @functools.wraps(f)
    def g():
        pass
    

    Is an alias for g = functools.update_wrapper(g, f). It does exactly three things:

    • it copies the __module__, __name__, __qualname__, __doc__, and __annotations__ attributes of f on g. This default list is in WRAPPER_ASSIGNMENTS, you can see it in the functools source.
    • it updates the __dict__ of g with all elements from f.__dict__. (see WRAPPER_UPDATES in the source)
    • it sets a new __wrapped__=f attribute on g

    The consequence is that g appears as having the same name, docstring, module name, and signature than f. The only problem is that concerning the signature this is not actually true: it is just that inspect.signature follows wrapper chains by default. You can check it by using inspect.signature(g, follow_wrapped=False) as explained in the doc. This has annoying consequences:

    • the wrapper code will execute even when the provided arguments are invalid.
    • the wrapper code can not easily access an argument using its name, from the received *args, **kwargs. Indeed one would have to handle all cases (positional, keyword, default) and therefore to use something like Signature.bind().

    Now there is a bit of confusion between functools.wraps and decorators, because a very frequent use case for developing decorators is to wrap functions. But both are completely independent concepts. If you're interested in understanding the difference, I implemented helper libraries for both: decopatch to write decorators easily, and makefun to provide a signature-preserving replacement for @wraps. Note that makefun relies on the same proven trick than the famous decorator library.

提交回复
热议问题