Another Implementation Method of decorator

Source: Internet
Author: User
This is all from a topic in the python-CN email list. In this topic, leopay provides another way to implement decorator:
Generally, decorator writes a nested function,
Def A (func ):
Def new_func (* ARGs, ** argkw ):
# Do some extra work
Return func (* ARGs, ** argkw) # Call the original function to continue processing
Return new_func
@
Def F (ARGs): Pass


In fact, there is another way of writing "Elegance:
Def A (func, * ARGs, ** argkw ):
# Do some extra work
Return func (* ARGs, ** argkw) # Call the original function to continue processing

@ A. _ GET __
Def F (ARGs): Pass

Very interesting, right. I analyzed the reason why this method works. It is very interesting. The special record is as follows.

Descriptor
Start with the simplest and most commonly used Semantic Expression in Python-attribute access. An expression like obj. Name is an attribute access. In the python compiled result, the corresponding bytecode command is load_attr. Attribute access seems to be a simple operation, but in terms of concept, it is divided into two relatively independent parts:
1. Attribute search
2. Get attributes
Attribute search searches for the symbol "name" in the _ dict _ of each base class of the object OBJ and obj. Of course, this search is ordered, in particular, when there are multiple base classes, it is not necessary to go into depth here. Once a symbol is found, the "attribute search" phase is completed, and the "attribute acquisition" phase is entered.
You can obtain the original attribute directly, for example, value = obj. what is the name in name and OBJ, and what is the value at the end. This "attribute acquisition" method exists in various programming languages, such as Java and C ++. This method is simple but not flexible enough. For example, one day, we want the semantics of value = obj. name to be "If name is empty, a special string is returned; otherwise, the name itself is returned ". Java and C ++ cannot easily handle such potential changes. Of course, we can use all obj. change name to OBJ. getname: Check the name through the function. However, once the project is built, the workload of this change can be imagined. Therefore, in Java or C ++, we recommend that you set the data to private and use public functions to access attributes. When the semantics of "attribute acquisition" changes, you only need to change the function. For example, the following method is used: Class ...{
Private string name;
Public getname ()...{
Return name;
}
}

This method can work, but it means that from the very beginning, we must wrap all the attributes through the function, which greatly increases the amount of code; and obj. the expression of getname is definitely not as good as obj. name comes naturally. So there is a second method. Is there a way to change only one code in the class so that all references to OBJ. name are transparently changed from simple assignment semantics to function call semantics. C # implements this method. Python also implements this method through descriptor.
To put it simply, when Python finds that the name_obj object corresponding to the symbol "name" is a special descriptor object after the attribute search is complete, the semantics of "Get attribute" is changed from directly returning name_obj to return name_obj. _ GET _ (OBJ, type (OBJ )). So what is a descriptor object? It is very simple. It implements _ Get __, _ set __, the instance object of the class of the _ del _ three special functions is a descriptor object. The following is a simple example: Class DESC (object ):
Def _ init _ (self, value ):
Self. value = Value

Def _ GET _ (self, OBJ, type ):
Print 'scriptor change something'
Return self. Value

Class A (object ):
Pass

A = ()
A. value = DESC (1)
Print A. Value

The output result is:
Descriptor change something
1

As a descriptor Function
Interestingly, a class object that implements _ GET _ is a descriptor only when it is a class attribute. When it is an attribute of an instance, it is not a descriptor. For example, if a. value = DESC (1) is replaced with a. value = DESC (1), the output result is <__ main _. DESC object at 0x00b000030>. Now we can look at the function from another perspective. A function is a descriptor object, which means that the _ GET _ operation is implemented in the function class. Are there classes for functions? Indeed, in Python, everything is an object, so a function is also an instance. Its Class corresponds to pyfunctionobject in Python source code. Through the following code, we can see that this pyfunctionobject is indeed a descriptor. Import types
Print types. functiontype. _ GET __

Output: <slot wrapper '_ GET _' of 'function' objects> corresponds to the func_descr_get function in Python source code. This shows that pyfunctionobject is indeed a descriptor. Why must it be a descriptor? The answer is oo.

Function and Method
OO in python is built on functions, which is similar to the object model in C ++. Consider the following class definition: Class A (object ):
Def show (self, name ):
Pirnt 'hello', name

Show is just a simple function, but you can never directly access this function, because there are only two ways to access show:
1. A = a (); A. Show ('Robert ')
2. A. Show (A (), 'Robert ')
Whether it is a. show or a. Show, note that they are all "attribute access. The show function is a descriptor object, so whether it is. show, or. show will all stimulate pyfunctiontype. _ GET _ (show, OBJ, type), for. for show, OBJ and type are a and a respectively. For. for show, OBJ and type are none and a respectively.
However, what we get now is not just a function, but a result converted by _ GET _. What is the result? It is a method object, simply put, this object is an object obtained after binding functions, classes, and instance objects. It has three attributes: im_func, im_self, and im_class. For pyfunctiontype. for the call results of _ GET _ (show, OBJ, type), the following relations exist: (im_func = show, im_self = OBJ, im_class = type ). Through the descriptor conversion, a function that is irrelevant to anyone is associated with an object. This is "binding ". Obviously, the method object is also a callable object. When we call method (* ARGs, ** argkw), it will be converted to im_func (im_self, * ARGs, ** argkw) to implement the semantics of the "member function" in OO.

Decorator
Decorator is actually a function modifier, such as def ():
Pass

@
Def B ():
Pass

After compilation, an action is actually added to B: B = A (B ). To save B in the context of a, a must adopt closure.
However, we can see that the key to implementing decorator is to save B, that is, to save a function. As we can see above, a function is saved in the method object, so it is all logical.
Previously, we saw that the _ GET _ action of descriptor is automatically triggered by "attribute access", but we can use. _ GET _ is directly triggered manually, so. _ GET _ (B) returns a method object, in which key function B is saved. When we perform. _ GET _ (B) (* ARGs, ** argkw) is converted to a (B, * ARGs, ** argkw ), originally, location B is left to an instance object to complete the semantics of the "member function" and implement Oo, but this is only a protocol, the position occupied by B can actually be passed into any object to complete any operation.
Finally, we wrote: @ A. _ GET __
Def B (* ARGs, ** argkw ):
Pass

In this form, let the python compilation result automatically complete the action of B = A. _ GET _ (B) for us. An "elegant" decorator form is born.

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.