Let's talk about the Python decorator mode and Aspect-Oriented Programming, python aspect

Source: Internet
Author: User
Tags python decorator

Let's talk about the Python decorator mode and Aspect-Oriented Programming, python aspect

Decorator mode and Aspect-Oriented Programming in Python


Let's discuss the decorator today. The decorator is a well-known design model and is often used in scenarios with cut-plane requirements. It is more classic in terms of log insertion, performance testing, and transaction processing. The decorator is an excellent design for solving such problems. With the decorator, we can extract a large number of identical codes irrelevant to the function itself and continue to reuse them. In summary, the purpose of the decorator is to add additional functions to existing objects.

1. decorator entry

1.1. How does the demand come from?


The definition of the decorator is very abstract. Let's look at a small example.

//edit http://www.lai18.com//date 2015-06-21def foo():    print 'in foo()' foo()


This is a boring function. But suddenly there was a more boring person. We called him B Jun and said, "I want to see how long it took to execute this function. Well, we can do this:

//edit http://www.lai18.com//date 2015-06-21import timedef foo():    start = time.clock()    print 'in foo()'    end = time.clock()    print 'used:', end - start foo()


Very good, functionality looks impeccable. But B suddenly didn't want to look at this function at the moment. He became more interested in another function named foo2.

What should we do? If you copy the newly added code to foo2, this makes a big mistake ~ Isn't copying anything the most annoying! What if B continues to read other functions? Reference Source:
Python decorator mode and Aspect-Oriented Programming
Http://www.lai18.com/content/433536.html

1.2. The token should be retained as it remains unchanged.


Remember, if a function is a first-class citizen in Python, we can consider redefining a function timeit, passing the reference of foo to it, and then calling foo in timeit for timing, in this way, we have achieved the goal of not modifying the definition of foo, and no matter how many functions B has read, we don't have to modify the definition of the function!

//edit http://www.lai18.com//date 2015-06-21import time def foo():    print 'in foo()' def timeit(func):    start = time.clock()    func()    end =time.clock()    print 'used:', end - start timeit(foo)


It seems that there is no logic problem. Everything is beautiful and works properly !...... Wait, we seem to have modified the code for calling. Originally we called: foo () in this way and changed it to: timeit (foo ). In this case, if foo is called at N, You have to modify the code at N. Or even more extreme, consider the situation where the code called somewhere cannot be modified. For example, this function is used by someone else.

1.3. minimize changes!

In this case, let's try to avoid modifying the called code. If you do not modify the called code, it means that calling foo () requires the effect of calling timeit (foo. We can think of assigning timeit to foo, but timeit seems to have a parameter ...... Try to unify the parameters! If timeit (foo) does not directly produce calling results, but returns a function consistent with the foo parameter list ...... It is easy to do. Assign the return value of timeit (foo) to foo. Then, the code that calls foo () does not need to be modified!

#-*-Coding: UTF-8-*-// edit http://www.lai18.com//date 2015-06-21import time def foo (): print 'in foo () '# define a timer, pass in, return another method def timeit (func) with the timing function appended: # define an embedded packaging function and add the packaging def wrapper () of the timing function to the input function (): start = time. clock () func () end = time. clock () print 'used: ', end-start # return the wrapped function return wrapper foo = timeit (foo) foo ()


In this way, a simple timer is ready! We only need to add foo = timeit (foo) before calling foo after defining foo to achieve the purpose of timing. This is the concept of the decorator, it looks like foo is decorated by timeit. In this example, the function requires timing when entering and exiting, which is called an Aspect. This Programming method is called Aspect-Oriented Programming ). Compared with the traditional top-down execution method, it seems that a logic is inserted horizontally in the function execution process. In a specific business area, a large amount of repeated code can be reduced. There are quite a few terms for Aspect-oriented programming. I will not introduce them more here. If you are interested, you can look for relevant materials.

This example is only used for demonstration and does not take into account the situation that foo has parameters and has returned values. The task of perfecting it is handed over to you :)

2. Additional Python support

2.1. Syntactic sugar


The above Code seems to be no longer streamlined, and Python provides a syntax sugar to reduce the input of characters.

import time def timeit(func):    def wrapper():        start = time.clock()        func()        end =time.clock()        print 'used:', end - start    return wrapper @timeitdef foo():    print 'in foo()' foo()


Focus on the @ timeit of the 11th rows. Adding this line in the definition is equivalent to writing foo = timeit (foo). Never think that @ has another magic power. In addition to fewer character input, there is an additional benefit: It looks more decorative.

2.2. built-in decorators

There are three built-in decorators: staticmethod, classmethod, and property. They are used to convert the instance methods defined in the class into static methods, class methods, and class attributes. Since functions can be defined in a module, static methods and class methods are not very useful unless you want to fully object-oriented programming. Attributes are not indispensable, and Java is also very moist without attributes. From my own Python experience, I have never used property, and the frequency of using staticmethod and classmethod is also very low.

class Rabbit(object):         def __init__(self, name):        self._name = name         @staticmethod    def newRabbit(name):        return Rabbit(name)         @classmethod    def newRabbit2(cls):        return Rabbit('')         @property    def name(self):        return self._name


The attribute defined here is a read-only attribute. If you need to be writable, You need to define another setter:

@name.setterdef name(self, name):    self._name = name


2.3. functools Module

The functools module provides two decorators. This module is added after Python 2.5. Generally, most users use a version later than this version. But my usual work environment is 2.4 T-T

2.3.1. wraps (wrapped [, assigned] [, updated]):

This is a very useful ornament. A friend who has read the previous reflection article should know that a function has several special attributes, such as the function name. After being decorated, the function name foo in the previous example will become the wrapper name of the packaging function, if you want to use reflection, unexpected results may occur. The decorator can solve this problem and retain the special attributes of the decorated functions.

import timeimport functools def timeit(func):    @functools.wraps(func)    def wrapper():        start = time.clock()        func()        end =time.clock()        print 'used:', end - start    return wrapper @timeitdef foo():    print 'in foo()' foo()print foo.__name__


First, pay attention to the row 5th. If you comment out this line, foo. _ name _ will be 'wrapper '. In addition, I believe you have noticed that this decorator has a parameter. In fact, there are two other optional parameters. Attribute names in assigned are replaced by values, while attribute names in updated are merged by update, you can obtain the default values by viewing the source code of functools. For this decorator, it is equivalent to wrapper = functools. wraps (func) (wrapper ).

2.3.2. total_ordering (cls ):

This decorator is useful in specific scenarios, but it is added after Python 2.7. It serves to add other comparison methods to the classes that implement at least _ lt _, _ le _, _ gt _, and _ ge _., this is a class decorator. If you do not understand the source code, take a closer look at the source code of the decorator:

def total_ordering(cls):      """Class decorator that fills in missing ordering methods"""      convert = {          '__lt__': [('__gt__', lambda self, other: other < self),                     ('__le__', lambda self, other: not other < self),                     ('__ge__', lambda self, other: not self < other)],          '__le__': [('__ge__', lambda self, other: other <= self),                     ('__lt__', lambda self, other: not other <= self),                     ('__gt__', lambda self, other: not self <= other)],          '__gt__': [('__lt__', lambda self, other: other > self),                     ('__ge__', lambda self, other: not other > self),                     ('__le__', lambda self, other: not self > other)],          '__ge__': [('__le__', lambda self, other: other >= self),                     ('__gt__', lambda self, other: not other >= self),                     ('__lt__', lambda self, other: not self >= other)]      }      roots = set(dir(cls)) & set(convert)      if not roots:          raise ValueError('must define at least one ordering operation: < > <= >=')      root = max(roots)       # prefer __lt__ to __le__ to __gt__ to __ge__      for opname, opfunc in convert[root]:          if opname not in roots:              opfunc.__name__ = opname              opfunc.__doc__ = getattr(int, opname).__doc__              setattr(cls, opname, opfunc)      return cls


This article is all over here. If I have time, I will sort out the source code of a modifier used to check the parameter type and put it up. It is an application :)

 

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.