The basic concept of adorners
As you know, adorners are a well-known design pattern that is often used in AOP (aspect-oriented programming) scenarios, with more classic insert logs, performance tests, transaction processing, Web permissions checks, caches, and so on.
The Python language itself provides the adorner syntax (@), and the typical adorner implementation is as follows:
@function_wrapperdef function (): Pass
@ is actually python2.4 's proposed syntax sugar, there is another equivalent implementation for the previous version of python2.4:
def function (): Passfunction = Function_wrapper (function)
two kinds of implementations of adorners
Function Wrapper-Classic implementation
def function_wrapper (wrapped): Def _wrapper (*args, **kwargs): Return wrapped (*args, **kwargs) return _wrapper @function_wrapperdef function (): Pass
Class wrapper-Easy to understand
Class Function_wrapper (object): Def __init__ (self, wrapped): self.wrapped = wrapped def __call__ (self, *args, **kwargs): Return self.wrapped (*args, **kwargs) @function_wrapperdef function (): Pass
functions (function) Introspection
When we talk about a function, we usually want the properties of the function to be clearly defined, such as name and Doc , as described in its documentation.
When you apply adorners to a function, the properties of the function change, but this is not what we expect.
def function_wrapper (wrapped): Def _wrapper (*args, **kwargs): Return wrapped (*args, **kwargs) return _wrapper @function_wrapperdef function (): Pass >>> print (function.__name__) _wrapper
The Python standard library provides functools.wraps () to solve this problem.
Import Functools def function_wrapper (wrapped): @functools. Wraps (wrapped) def _wrapper (*args, **kwargs): Retu RN Wrapped (*args, **kwargs) return _wrapper @function_wrapperdef function (): Pass >>> print (function.__name __) function
However, when we want to get the parameter (argument) of the wrapper function or source code, we also can't get the result we want.
Import Inspect Def function_wrapper (wrapped): ... @function_wrapperdef function (arg1, arg2): Pass >>> print ( Inspect.getargspec (function)) Argspec (args=[], varargs= ' args ', keywords= ' Kwargs ', Defaults=none) >>> print ( Inspect.getsource (function)) @functools. Wraps (wrapped) def _wrapper (*args, **kwargs): Return Wrapped (*args, * *kwargs
wrapper class Method (@classmethod)
When the wrapper (@function_wrapper) is applied to @classmethod, the following exception will be thrown:
Class Class (Object): @function_wrapper @classmethod def cmethod (CLS): Pass Traceback (most recent call Las T): File ' <stdin> ', line 1, in <module> file ' <stdin> ', line 3, ' in ' Class File ' <stdin> ', line 2 , in wrapper File ".../functools.py", line-up, in Update_wrapper setattr (wrapper, attr, GetAttr (wrapped, attr)) Attribu Teerror: ' Classmethod ' object has no attribute ' __module__ '
Because @classmethod is implemented, some of the properties required by Functools.update_wrapper are missing. This is the bug,3.2 version of Functools.update_wrapper in Python2 has been fixed, refer to http://bugs.python.org/issue3445.
However, under Python3, another problem arose:
Class Class (Object): @function_wrapper @classmethod def cmethod (CLS): Pass >>> Class.cmethod () Tr Aceback (most recent): File "classmethod.py", line <module> class.cmethod () file "Classmethod. Py ", line 6, in _wrapper return wrapped (*args, **kwargs) TypeError: ' Classmethod ' object was not callable
This is because the wrapper determines that the packaged function (@classmethod) can be called directly, but the fact is not necessarily the case. The wrapped function may actually be a descriptor (descriptor), meaning that the function (descriptor) must be properly bound to an instance in order to make it callable. For the definition of descriptors, refer to https://docs.python.org/2/howto/descriptor.html.
Summary-Simplicity does not mean that the right
Although the methods used by people to implement adorners are usually simple, this does not mean that they must be correct and always work correctly.
As we have seen above, functools.wraps () can help us solve the name and Doc problems, but it is useless to get the arguments (argument) or source code of the function.
The above problem, WRAPT can help to solve, detailed usage can refer to its official document: http://wrapt.readthedocs.org
The writer is a ONEAPM engineer who is keen to read more technical articles, please visit the ONEAPM Official technology blog.
Python-An error in the use of adorners