Some suggestions for using Python decorators

Source: Internet
Author: User
This article mainly introduces some suggestions for using the Python decorators. The decorators are an important part of Python learning. For more information, see Basic decorator concepts

As we all know, the decorator is a well-known design model and is often used in AOP (cross-section programming) scenarios. it is more classic in terms of log insertion, performance testing, and transaction processing, web permission verification and Cache.

The Python language provides the decorator syntax (@). The typical decorator implementation is as follows:

  @function_wrapper  def function():    pass

@ Actually, it is the syntactic sugar proposed by python2.4. for python2.4 earlier versions, there is another equivalent implementation:

  def function():    pass  function = function_wrapper(function)

Two implementations of the decorator

Function wrapper-classic implementation

  def function_wrapper(wrapped):    def _wrapper(*args, **kwargs):      return wrapped(*args, **kwargs)    return _wrapper   @function_wrapper  def function():    pass

Package-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_wrapper  def function():    pass

Function introspection

When talking about a function, we usually want its attributes to be clearly defined as described in the document, such as _ name _ and _ doc __.

The attributes of a function are changed when the decorator is applied to a function, but this is not what we expect.

  def function_wrapper(wrapped):    def _wrapper(*args, **kwargs):      return wrapped(*args, **kwargs)    return _wrapper   @function_wrapper  def 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):      return wrapped(*args, **kwargs)    return _wrapper   @function_wrapper  def function():    pass   >>> print(function.__name__)  function

However, when we want to obtain the parameters (argument) or source code of the encapsulated function, we cannot get the expected results.

  import inspect   def function_wrapper(wrapped): ...  @function_wrapper  def 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)

Packaging method (@ classmethod)

When the package (@ function_wrapper) is applied to @ classmethod, the following exception is thrown:

  class Class(object):    @function_wrapper    @classmethod    def cmethod(cls):      pass   Traceback (most recent call last):   File "
 
  ", line 1, in 
  
      File "
   
    ", line 3, in Class   File "
    
     ", line 2, in wrapper   File ".../functools.py", line 33, in update_wrapper    setattr(wrapper, attr, getattr(wrapped, attr))  AttributeError: 'classmethod' object has no attribute '__module__'
    
   
  
 

Because the implementation of @ classmethod lacks certain attributes required by functools. update_wrapper. This is a bug in functools. update_wrapper in python2. version 3.2 has been fixed. for details, refer to http://bugs.python.org/issue3445.

However, another problem occurs during execution in python3:

  class Class(object):    @function_wrapper    @classmethod    def cmethod(cls):      pass   >>> Class.cmethod()   Traceback (most recent call last):   File "classmethod.py", line 15, in 
 
      Class.cmethod()   File "classmethod.py", line 6, in _wrapper    return wrapped(*args, **kwargs)  TypeError: 'classmethod' object is not callable
 

This is because the wrapper determines that the encapsulated function (@ classmethod) can be called directly, but this is not necessarily the case. The encapsulated function may actually be a descriptor, which means that the function (descriptor) must be correctly bound to an instance for its call. For the descriptor definition, refer to https://docs.python.org/2/howto/descriptor.html.
Summary-simplicity does not mean correctness

Although the method used to implement the decorator is usually very simple, it does not mean that they must be correct and always work properly.

As we can see above, functools. wraps () can help us solve the _ name _ and _ doc _ problems, but it is helpless to obtain function parameters (argument) or source code.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.