A detailed description of the adorner usage in Python

Source: Internet
Author: User
The example in this article describes the adorner usage in Python. Share to everyone for your reference. The specific analysis is as follows:

Let's start with a question above stackoverflow, if you use the following code:

The code is as follows:

@makebold
@makeitalic
Def say ():
Return "Hello"


Print out the following output:

Hello
What would you do? The final answer is:

The code is as follows:

def makebold (FN):
Def wrapped ():
Return "" + fn () + ""
return wrapped

def makeitalic (FN):
Def wrapped ():
Return "" + fn () + ""
return wrapped

@makebold
@makeitalic
def hello ():
Return "Hello World"

print Hello () # # return to Hello World


Now let's look at how to understand Python's adorners from some of the most basic ways.

The code is as follows:

def foo ():
print ' in foo () '
Foo ()


This is a very boring function, yes. But suddenly there is a more boring person, we call him B-June, said I want to see how long it takes to execute this function, OK, then we can do this:

The code is as follows:

Import time
def foo ():
Start = Time.clock ()
print ' in foo () '
End = Time.clock ()
print ' used: ', End-start

Foo ()


Very well, the features look impeccable. But the egg-ache B-June suddenly don't want to see this function, he to another called Foo2 's function produced a more intense interest.

What do we do? If the above new additions to the code to copy into the Foo2, which made a big taboo ~ Copy what is not the most annoying it! And what if B-June continues to look at the other functions?

1.2. Status quo, is change also

Remember, the function is a one-time citizen in Python, so we can consider redefining a function Timeit, passing the Foo reference to him, then calling Foo in Timeit and timing it so that we do not have to change the Foo definition, and, No matter how many functions B has seen, we don't have to modify the function definition!

The code is as follows:

Import time

def foo ():
print ' in foo () '

def Timeit (func):
Start = Time.clock ()
Func ()
End =time.clock ()
print ' used: ', End-start

Timeit (foo)


There seems to be no logical problem, everything is fine and functioning properly! ...... Wait, we seem to have modified the code for the calling section. Originally we called this: Foo (), modified to become: Timeit (foo). In this case, if Foo is called at N, you will have to modify the code in the N place. Or, to be more extreme, consider that the code in which it is called cannot modify the situation, for example: This function is something you give to someone else.

1.3. Minimize Changes!

That being the case, we'll try to figure out how to do this without modifying the calling code, which 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 ... Think of ways to unify the parameters! If Timeit (foo) does not directly produce a call effect, but instead returns a function that is consistent with the Foo argument list ... It's good to have the return value of Timeit (foo) assigned to Foo, and then the code that calls Foo () doesn't have to be modified at all!

The code is as follows:

#-*-Coding:utf-8-*-
Import time

def foo ():
print ' in foo () '

# define a timer, pass in one, and return another method with the timer feature attached
def Timeit (func):

# define an inline wrapper function that adds a timing function to the incoming function
def wrapper ():
Start = Time.clock ()
Func ()
End =time.clock ()
print ' used: ', End-start

# Returns the Wrapped function
Return wrapper

foo = Timeit (foo)
Foo ()


In this way, a simple timer is ready! We only need to call Foo after the definition of foo, and foo = Timeit (foo), to achieve the purpose of timing, which is the concept of adorners, which looks like Foo was Timeit decorated. In this example, the function enters and exits with timing, which is called a cross plane (Aspect), which is called aspect-oriented programming (Aspect-oriented programming). In comparison to the top-down execution of traditional programming habits, a logic is inserted horizontally in the process of function execution. In a specific business area, you can reduce the amount of repetitive code. Aspect-oriented programming there are quite a lot of terminology, here is not much to do introduction, interested in the words can find relevant information.

This example is for demonstration purposes only, and does not consider Foo with parameters and a return value of the case, the task of perfecting it to you:)

The above code seems to be no longer streamlined, and Python provides a syntactic sugar to reduce the amount of character input.

The code is as follows:

Import time

def Timeit (func):
def wrapper ():
Start = Time.clock ()
Func ()
End =time.clock ()
print ' used: ', End-start
Return wrapper

@timeit
def foo ():
print ' in foo () '

Foo ()


Focus on the 11th line of @timeit, add this line to the definition and the other write foo = Timeit (foo) is completely equivalent, do not think that @ has another magic. In addition to the less character input, there is an additional benefit: it looks more like a decorator.

To understand Python's adorners, we must first understand that functions are also considered objects in Python. This is important. Let's look at an example:

The code is as follows:

def shout (word= "yes"):
return word.capitalize () + "!"

Print Shout ()
# output: ' Yes! '

# as an object, you can assign a function to any other object variable

Scream = Shout

# Note that we're not using parentheses because we're not calling the function
# We assign the function shout to scream, which means you can call shout by scream

Print Scream ()
# output: ' Yes! '

# Also, you can delete the old name shout, but you can still access the function via scream

Del shout
Try:
Print Shout ()
Except Nameerror, E:
Print E
#输出: "Name ' shout ' is not defined"

Print Scream ()
# output: ' Yes! '


Let's put this topic aside for the moment, let's take a look at Python's other interesting property: You can define a function in a function:

The code is as follows:

Def talk ():

# You can define another function in the talk
DEF Whisper (word= "yes"):
return Word.lower () + "...";

# ... and use it immediately.

Print Whisper ()

# Every time you call ' Talk ', the whisper defined in the talk will also be called
Talk ()
Output
# Yes ...

# but "Whisper" does not exist alone:

Try:
Print Whisper ()
Except Nameerror, E:
Print E
#输出: "Name ' whisper ' is not defined" *


Function reference

From the above two examples we can conclude that since the function as an object, therefore:

1. It can be assigned to other variables

2. It can be defined within another function

This means that the function can return a function, see the following example:

The code is as follows:

def gettalk (type= "Shout"):

# we define another function
def shout (word= "yes"):
return word.capitalize () + "!"

DEF Whisper (word= "yes"):
return Word.lower () + "...";

# and then we go back to one of them
if type = = "Shout":
# We're not using (), because we're not calling the function
# We're returning the function
return shout
else:
return Whisper

# and then how to use it?

# Assign the function to a variable
talk = Gettalk ()

# Here you can see that talk is actually a function object:
Print talk
#输出:

# One of the objects returned by the function is the object:
Print talk ()

# or you can call directly as follows:
Print Gettalk ("Whisper") ()
#输出: Yes ...


Also, since it is possible to return a function, we can pass it as a parameter to the function:

The code is as follows:

def dosomethingbefore (func):
Print "I do something before then I call the function you gave me"
Print func ()

Dosomethingbefore (Scream)
#输出:
#I do something before then I call the function you gave me
#Yes!


Here you have enough to understand the adorner, and other it can be considered a wrapper. That is, it allows you to execute code before and after decorating without changing the contents of the function itself.

Handmade decoration

So how do you do manual decorating?

The code is as follows:

# Adorner is a function, and its argument is another function
def my_shiny_new_decorator (a_function_to_decorate):

# Another function is defined internally: a wrapper.
# This function encapsulates the original function, so you can execute some code before or after it
Def the_wrapper_around_the_original_function ():

# put some code that you want before the real function executes
Print "Before the function runs"

# Execute original function
A_function_to_decorate ()

# put some code that you want after the original function executes
Print "After the function runs"

#在此刻, "A_function_to_decrorate" has not been executed yet, we have returned the wrapper function created
#封装器包含了函数以及其前后执行的代码, it's ready.
Return the_wrapper_around_the_original_function

# Now imagine that you've created a function that you'll never be far away again
Def a_stand_alone_function ():
Print "I am a stand alone function, don ' t you dare modify Me"

A_stand_alone_function ()
#输出: I am A stand alone function, don ' t you dare modify me

# Well, you can encapsulate the extension of its implementation behavior. You can simply throw it to the decorator.
The adorner will dynamically encapsulate it with the code you want, and return a new available function.
a_stand_alone_function_decorated = My_shiny_new_decorator (a_stand_alone_function)
A_stand_alone_function_decorated ()
#输出:
#Before the function runs
#I am a stand alone function, don ' t you dare modify me
#After the function runs


Now you may require that the actual call is a_stand_alone_function_decorated every time you call a_stand_alone_function. Implementation is also very simple, you can use My_shiny_new_decorator to re-assign the value of A_stand_alone_function.

The code is as follows:

A_stand_alone_function = My_shiny_new_decorator (a_stand_alone_function)
A_stand_alone_function ()
#输出:
#Before the function runs
#I am a stand alone function, don ' t you dare modify me
#After the function runs

# and guess what, that's exactly what decorators do!


Decorator Secrets

In the previous example, we can use the adorner syntax:

The code is as follows:

@my_shiny_new_decorator
Def another_stand_alone_function ():
Print "Leave Me Alone"

Another_stand_alone_function ()
#输出:
#Before the function runs
#Leave me alone
#After the function runs


Of course you can also accumulate decorations:

The code is as follows:

def Bread (func):
def wrapper ():
Print " "
Func ()
Print "<\______/>"
Return wrapper

Def ingredients (func):
def wrapper ():
print "#tomatoes #"
Func ()
Print "~salad~"
Return wrapper

def sandwich (food= "--ham--"):
Print Food

Sandwich ()
#输出:--ham--
Sandwich = Bread (Ingredients (sandwich))
Sandwich ()
#outputs:
#
# #tomatoes #
#--ham--
# ~salad~
#<\______/>


Using the Python adorner syntax:

The code is as follows:

@bread
@ingredients
def sandwich (food= "--ham--"):
Print Food

Sandwich ()
#输出:
#
# #tomatoes #
#--ham--
# ~salad~
#<\______/>


the order of the adorners is important, and you need to be aware

The code is as follows:

@ingredients
@bread
def strange_sandwich (food= "--ham--"):
Print Food

Strange_sandwich ()
#输出:
# #tomatoes #
#
#--ham--
#<\______/>
# ~salad~


Finally, answer the questions mentioned above:

The code is as follows:

# Adorner makebold for converting to bold
def makebold (FN):
# result returns the function
def wrapper ():
# Insert some code before and after execution
Return "" + fn () + ""
Return wrapper

# Adorner makeitalic for converting to italic
def makeitalic (FN):
# result returns the function
def wrapper ():
# Insert some code before and after execution
Return "" + fn () + ""
Return wrapper

@makebold
@makeitalic
Def say ():
return "Hello"

Print Say ()
#输出: Hello

# Equal To
Def say ():
return "Hello"
Say = Makebold (Makeitalic (say))

Print Say ()
#输出: Hello


built -in adorners

There are three built-in decorators, Staticmethod, Classmethod, and property, respectively, to make the instance methods defined in the class into static, class, and class properties. Because functions can be defined in a module, static and class methods are not much useful unless you want to fully object-oriented programming. Attributes are not essential, and Java does not have the properties to live very well. From my personal Python experience, I have not used the property, and the frequency of using Staticmethod and Classmethod is very low.

The code is as follows:

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 property, and if it is writable, a setter needs to be defined:

The code is as follows:

@name. Setter
def name (self, name):
Self._name = Name


Functools Module

The Functools module provides two adorners. This module was added after Python 2.5, and generally everyone should use more than this version. But my usual working environment is 2.4 t-t

2.3.1. Wraps (wrapped[, assigned][, updated]):
This is a very useful decorator. A friend who has seen the previous reflection should know that the function has several special properties such as the function name, after being decorated, the function name Foo in the above example becomes the name of the wrapper function wrapper, and if you want to use reflection, it can result in unexpected results. This decorator solves this problem by preserving the special properties of the decorated function.

The code is as follows:

Import time
Import Functools

def Timeit (func):
@functools. Wraps (func)
def wrapper ():
Start = Time.clock ()
Func ()
End =time.clock ()
print ' used: ', End-start
Return wrapper

@timeit
def foo ():
print ' in foo () '

Foo ()
Print foo.__name__


Notice the 5th line first, and if you comment on this line, foo.__name__ will be ' wrapper '. In addition, I believe you have noticed that this decorator has a parameter. In fact, he has two other optional arguments, the property names in assigned are replaced with assignments, and the property names in updated are merged using update, and you can get their default values by looking at the source code of Functools. For this adorner, it is equivalent to wrapper = Functools.wraps (func) (wrapper).

2.3.2. Total_ordering (CLS):
This adorner is useful for certain occasions, but it is added after Python 2.7. Its role is to implement at least __lt__, __le__, __gt__, __ge__ one of the classes plus other comparison methods, this is a class adorner. If you feel bad, take a closer look at the source code of this adorner:

The code is as follows:

def total_ordering (CLS):
"" "Class decorator, 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-<= 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->= 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

Hopefully this article will help you with Python programming.

  • 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.