The focus of the Python decorator

Source: Internet
Author: User
Tags benchmark extend scream wrapper snipe python decorator in python

Adorner Basics
The Python function is an object
To understand adorners, you must first understand that in the Python world, functions are objects. This is important. Let's take a simple example to see why:

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

Print Shout ()
# output: ' yes! '

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

Scream = Shout

# Note that we don't use parentheses:
# We didn't call the function, we loaded the ' shout ' function into the ' Scream ' variable
# That means you can call ' shout ' like from ' Scream '

Print Scream ()
# output: ' yes! '

# not only that, it also means you can delete the old name ' shout ',
# and you can still access the function through ' scream '

Del shout
Try
Print Shout ()
Except Nameerror, E:
Print E
# output: ' name ' shout ' is not defined '

Print Scream ()
# output: ' yes! '
okay! Keep this concept in mind. We'll use it later.

Another interesting attribute of Python functions is that they can be defined inside another function ...

Def talk ():

# You can define a function inside the ' Talk ' function at run time
DEF Whisper (word= "yes"):
return Word.lower () + "..."

# ... and use it immediately.

Print Whisper ()

# when "Talk" is invoked, every call you make will define "whisper" one time,
# then call ' Whisper '
Talk ()
Output
# "Yes ..."

# However, "whisper" does not actually exist outside of the "talk" function

Try
Print Whisper ()
Except Nameerror, E:
Print E
#输出: "Name ' whisper ' is not defined"
#Python的函数是对象
Function reference
Do you still have it? Now let's talk about the fun part ...

You've seen it. A function is an object. So

function can be assigned to a variable
Functions can be defined in another function
This means that the function can return another function. Watch it carefully!

def gettalk (kind= "Shout"):

# We define functions at run time
def shout (word= "yes"):
return word.capitalize () + "!"

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

# and then we go back to one of them
if kind = = "shout":
# do not apply parentheses, we are not calling functions
# We're returning a function object
return shout
Else
return Whisper

# How are you going to use this giant beast?

# Gets the function and assigns it to a variable
talk = Gettalk ()

# here, you can see ' Talk ' is a function object
Print talk
#输出: <function Shout at 0xb7ea817c>

# The following object is a return of the function ' Talk '
Print talk ()
#输出: yes!

# You can even use it directly
# and can even use it directly if you feel wild:
Print Gettalk ("Whisper") ()
#输出: Yes ...
Wait a minute... More than that!

If you want to return a function, you can pass in a function as an argument:

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

Dosomethingbefore (Scream)
#输出:
#I do something before then I call the function for you gave me
#Yes!
Now you have all the knowledge you need to understand the adorner. As you can see, the adorner is "wrapping paper (wrapper)", which means that the adorner allows you to execute code before and after the decorated function, without making any changes to the function itself.

Write the Decorator
You'd better write a letter (decorator):

# Adorner is a function that takes another function as an argument
def my_shiny_new_decorator (a_function_to_decorate):

# inside the adorner, define a ' wrapper ' function
# The wrapper function is used to wrap the original function to execute code before and after the original function
Def the_wrapper_around_the_original_function ():

# Enter the code you want to execute before calling the original function here
Print "Before the function runs"

# Call the Decorated function (is the function call, need parentheses)
A_function_to_decorate ()

# Enter the code you want to execute after calling the original function here
Print "After the function runs"

# here, the decorated function has not been executed
# return the wrapper function you just created
# wrapper functions include decorated functions, code executed before and after the function
Return the_wrapper_around_the_original_function

# Now, suppose you create a function that may not be used
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

# However, you can decorate it, expand it
# just to pass it to an adorner, the adorner will dynamically wrap the function into whatever code you want.
# and returns a new function that is available

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 want to invoke a_stand_alone_function_decorated each time you invoke a_stand_alone_function. Quite simply, the function returned using the adorner my_shiny_new_decorator overwrites the original function a_stand_alone_function.

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

# That's how the decorator works.
The revelation of the decoration device
Using the adorner syntax to reproduce the previous example, this is true:

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

Another_stand_alone_function (
#outputs:
#Before the function runs
#Leave me alone
#After the function runs
Yes, that's it, it's so simple. @decorator is an abbreviation for the following form:

Another_stand_alone_function = My_shiny_new_decorator (another_stand_alone_function)
The adorner described here is a Python variant of the adorner design pattern. There are some classic design patterns in Python that make it easier to develop (such as iterators (iterator)).

Of course, you can overlay the adorner:

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 ()
#输出:
#</' "' \>
# #tomatoes #
#--ham--
# ~salad~
#<\______/>
Syntax for the python adorner:

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

Sandwich ()
#输出:
#</' "' \>
# #tomatoes #
#--ham--
# ~salad~
#<\______/>
The order in which you set up adorners is important:

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

Strange_sandwich ()
#输出:
# #tomatoes #
#</' "' \>
#--ham--
#<\______/>
# ~salad~
Now: For your question (in Python, how to implement a cascade adorner) ...
(Translator Note: This is StackOverflow last answer)

The conclusion is that, as you can see, it is easy to implement Cascade adorners:

# bold Adorner
def makebold (FN):
# The new function returned by the adorner
def wrapper ():
# Insert some code before and after
Return "<b>" + fn () + "</b>"
Return wrapper

# italic Adorner
def makeitalic (FN):
# The new function returned by the adorner
def wrapper ():
# Insert some code before and after
Return "<i>" + fn () + "</i>"
Return wrapper

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

Print Say ()
#输出: <b><i>hello</i></b>

# is equivalent to
Def say ():
return "Hello"
Say = Makebold (Makeitalic (say))

Print Say ()
#输出: <b><i>hello</i></b>
Now, you can leave happily (translator Note: This is what the author says to the questioner). Or then burn your brain to see some advanced usage of the adorner.

Adorner advanced
Passing parameters to the decorated function
# It's not black magic, you just need to pass the parameters in the wrapping process

def a_decorator_passing_arguments (function_to_decorate):
def a_wrapper_accepting_arguments (Arg1, arg2):
Print "I got args! Look: ", Arg1, arg2
Function_to_decorate (Arg1, arg2)
Return a_wrapper_accepting_arguments

# When you call the function returned by the adorner, you call the wrapping paper (wrapper)
# The parameters passed to wrapping paper will continue to be passed to the decorated function

@a_decorator_passing_arguments
def print_full_name (first_name, last_name):
Print "My name is", first_name, last_name

Print_full_name ("Peter", "Venkman")
Output
#I got args! Look:peter Venkman
#My name is Peter Venkman
Decoration method
The good thing about Python is that methods and functions (function) are actually the same. The only difference is that the first parameter of the method needs to be a reference to the current object (self).

This means that you can create a method in the same way as an adorner! But don't forget self.

def method_friendly_decorator (method_to_decorate):
def wrapper (self, Lie):
Lie = lie-3 # It's good to be younger:-)
Return Method_to_decorate (self, Lie)
Return wrapper


Class Lucy (object):

def __init__ (self):
Self.age = 32

@method_friendly_decorator
def sayyourage (self, Lie):
Print "I am%s, what did"% (Self.age + Lie)

L = Lucy ()
L.sayyourage (-3)
#输出: I am, what did you?
If you want to write a generic adorner – available to any function or method, regardless of its parameters – then, with *args, **kwargs is fine:

def a_decorator_passing_arbitrary_arguments (function_to_decorate):
# wrapping paper receives any parameter
def a_wrapper_accepting_arbitrary_arguments (*args, **kwargs):
Print "Do I have args?:"
Print args
Print Kwargs
# extract the resulting parameters (unpack), here for *args, **kwargs
# If you are not familiar with the decompression, refer to
# http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/
Function_to_decorate (*args, **kwargs)
Return a_wrapper_accepting_arbitrary_arguments

@a_decorator_passing_arbitrary_arguments
Def function_with_no_argument ():
Print "Python is cool and no argument here."

Function_with_no_argument ()
#输出
#Do I have args?:
#()
#{}
#python就是这么酷, there is no ginseng here.

@a_decorator_passing_arbitrary_arguments
Def function_with_arguments (A, B, c):
Print A, B, c

Function_with_arguments (1,2,3)
#输出
#Do I have args?:
# (1, 2, 3)
#{}
#1 2 3

@a_decorator_passing_arbitrary_arguments
Def function_with_named_arguments (A, B, C, platypus= "Why not?"):
print ' Do%s ',%s and%s like platypus? %s '%\
(A, B, C, platypus)

Function_with_named_arguments ("Bill", "Linus", "Steve", platypus= "indeed!")
#输出
#Do I have args? :
# (' Bill ', ' Linus ', ' Steve ')
#{' platypus ': ' indeed! '}
#Bill, Linus and Steve like the platypus? True!

Class Mary (object):

def __init__ (self):
Self.age = 31

@a_decorator_passing_arbitrary_arguments
def sayyourage (self, lie=-3): # Now you can add a default value
Print "I am%s, what did"% (Self.age + Lie)

m = Mary ()
M.sayyourage ()
#输出
# do I have args?:
# (<__main__. Mary object at 0xb7d303ac>,)
#{}
#I am, what did you?
Passing parameters to adorners
Well, now how do you feel about passing parameters to the adorner itself?

This is a bit around because the adorner must receive a function as an argument. Therefore, you cannot pass the parameters of the decorated function directly to the adorner.

Before explaining the solution, let's start with a little review:

# adorners are normal functions
def my_decorator (func):
Print "I am an ordinary function"
def wrapper ():
Print "I am function returned by the decorator"
Func ()
Return wrapper

# So, you can call it without ' @ '

Def lazy_function ():
Print "Zzzzzzzz"

Decorated_function = My_decorator (lazy_function)
#输出: I am an ordinary function

# It will output ' I am an ordinary function ' because that's what you do:
# Call a letter. There's no magic in there.

@my_decorator
Def lazy_function ():
Print "Zzzzzzzz"

#输出: I am an ordinary function
The above two are exactly the same, all are called My_decorator. So when you @my_decorator, you're telling Python to call a function that is marked by a variable my_decorator.

This is important! You can specify the adorner directly with the label, or not.

Let's go a little deeper.

Def decorator_maker ():

print ' I make decorators! I am executed only once: "+\
"When you are make me create a decorator."

def my_decorator (func):

Print "I am a decorator! I am executed only if you decorate a function. "

Def wrapped ():
Print ("I am" the wrapper around the decorated function.)
"I am called when to call the decorated function."
"As the wrapper, I return the result of the decorated function."
return func ()

Print "As the decorator, I return the wrapped function."

return wrapped

Print "As a decorator maker, I return a decorator"
Return My_decorator

# Let's create an adorner
New_decorator = Decorator_maker ()
Output
#I make decorators! I am executed only once:when your make me create a decorator.
#As a decorator maker, I return a decorator

# Next, we'll decorate the function

Def decorated_function ():
Print "I am the decorated function."

Decorated_function = New_decorator (decorated_function)
#输出:
#I am a decorator! I am executed only if you decorate a function.
#As the decorator, I return the wrapped function

# Call Function:
Decorated_function ()
#输出:
#I am the wrapper around the decorated function. I am called when to call the decorated function.
#As the wrapper, I return the result of the decorated function.
#I am the decorated function.
There's nothing to be surprised about here.

Skipping all the tedious intermediate variables, the following is exactly the same as the above method.

Def decorated_function ():
Print "I am the decorated function."
Decorated_function = Decorator_maker () (decorated_function)
#输出:
#I make decorators! I am executed only once:when your make me create a decorator.
#As a decorator maker, I return a decorator
#I am a decorator! I am executed only if you decorate a function.
#As the decorator, I return the wrapped function.

Last
Decorated_function ()
#输出:
#I am the wrapper around the decorated function. I am called when to call the decorated function.
#As the wrapper, I return the result of the decorated function.
#I am the decorated function.
You can also streamline a few:

@decorator_maker ()
Def decorated_function ():
Print "I am the decorated function."
#outputs:
#I make decorators! I am executed only once:when your make me create a decorator.
#As a decorator maker, I return a decorator
#I am a decorator! I am executed only if you decorate a function.
#As the decorator, I return the wrapped function.

#Eventually:
Decorated_function ()
#outputs:
#I am the wrapper around the decorated function. I am called when to call the decorated function.
#As the wrapper, I return the result of the decorated function.
#I am the decorated function.
Hey, did you see that? We use functions through the "@" syntax.

So, back to the adorner with the parameter. If we could use a function to generate an adorner at runtime, we would be able to pass arguments to that function, right?

def decorator_maker_with_arguments (Decorator_arg1, DECORATOR_ARG2):

print ' I make decorators! and I Accept arguments: ", DECORATOR_ARG1, DECORATOR_ARG2

def my_decorator (func):
# The ability to pass parameters here, thanks to the nature of closures
# If you're not used to closures, you can assume that it's OK, or look at this article:
# Http://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python
Print "I am the decorator." Somehow you passed me arguments: ", DECORATOR_ARG1, DECORATOR_ARG2

# Don't confuse adorner parameters with function parameters!


def wrapped (Function_arg1, FUNCTION_ARG2):


Print ("I am" the wrapper around the decorated function.\n "


"I can access all" variables\n "


"\t-from the Decorator: {0} {1}\n"


"\t-from the function call: {2} {3}\n"


"Then I can pass them to the decorated function"


. Format (DECORATOR_ARG1, DECORATOR_ARG2,


FUNCTION_ARG1, FUNCTION_ARG2))


return func (FUNCTION_ARG1, FUNCTION_ARG2)

return wrapped

Return My_decorator

@decorator_maker_with_arguments ("Leonard", "Sheldon")
def decorated_function_with_arguments (Function_arg1, FUNCTION_ARG2):
Print ("I am" decorated function and only knows about my arguments: {0} "
' {1} '. Format (FUNCTION_ARG1, FUNCTION_ARG2)

Decorated_function_with_arguments ("Rajesh", "Howard")
#输出:
#I make decorators! and I Accept Arguments:leonard Sheldon
#I am the decorator. Somehow you passed me arguments:leonard Sheldon
#I am the wrapper around the decorated function.
#I can access all the variables
#-From the Decorator:leonard Sheldon
#-From the function Call:rajesh Howard
#Then I can pass them to the decorated function
#I am the decorated function and knows about my Arguments:rajesh Howard
This is the adorner with the ginseng. Parameters can also be set to variables:

C1 = "Penny"
C2 = "Leslie"

@decorator_maker_with_arguments ("Leonard", C1)
def decorated_function_with_arguments (Function_arg1, FUNCTION_ARG2):
Print ("I am" decorated function and only knows about my arguments: "
' {0} {1} '. Format (FUNCTION_ARG1, FUNCTION_ARG2))

Decorated_function_with_arguments (C2, "Howard")
#输出:
#I make decorators! and I Accept Arguments:leonard Penny
#I am the decorator. Somehow you passed me arguments:leonard Penny
#I am the wrapper around the decorated function.
#I can access all the variables
#-From the Decorator:leonard Penny
#-From the function Call:leslie Howard
#Then I can pass them to the decorated function
#I am the decorated function and knows about my Arguments:leslie Howard
As you can see, using this technique, you could pass parameters to the adorner like any (normal) function. If you want, you can even use *args, **kwargs. However, remember that the adorner can only be invoked 1 times, when the script is imported. Then you can't set the parameters dynamically. When you "Import X", the function has been decorated, you can not make any changes.

Practice: Decorating the decorative device
As a benefit, I will give you an adorner snippet that can receive any parameters. Then we use another function to create our adorner to receive the parameters.

We packaged the adorner.

As we've seen before, what do we use to wrap a function?

Yes, that's the decorator!

Let's happily write an adorner for the adorner:

def Decorator_with_args (decorator_to_enhance):
"""
This function is used as an adorner.
It has to decorate another function, and that function is also an adorner
It allows any adorner to receive any number of parameters so that it doesn't have to be recalled every time you write (an adorner).
"""

# using the same technique as above passing parameters
def decorator_maker (*args, **kwargs):

# We created an adorner at run time that receives a function as an argument
# but it can hold the parameters from the maker
def decorator_wrapper (func):

# We return the results of the original adorner, a normal function
# The only thing that's wrong is that the adorner has to work.
Return Decorator_to_enhance (func, *args, **kwargs)

Return Decorator_wrapper

Return Decorator_maker
It can be used as follows:

# You can create a function to use as an adorner. and decorate it with an adorner.
# Don't forget, the usage is "decorator (func, *args, **kwargs)"
@decorator_with_args
Def decorated_decorator (func, *args, **kwargs):
def wrapper (Function_arg1, FUNCTION_ARG2):
Print "decorated with", args, Kwargs
return func (FUNCTION_ARG1, FUNCTION_ARG2)
Return wrapper

# Now, you can decorate the function with the decorated adorner you just created.

@decorated_decorator (42, 404, 1024)
def decorated_function (Function_arg1, FUNCTION_ARG2):
Print "Hello", Function_arg1, FUNCTION_ARG2

Decorated_function ("Universe and", "everything")
#输出:
#Decorated with (42, 404, 1024) {}
#Hello Universe and everything

Wow!
I know that the last time you had this feeling was to hear someone say, "To understand recursion, you have to understand recursion first." Now, for the learned (adorners), you do not feel good code?

Best Practice: adorners
Adorners first appear in Python2.4, so make sure your code runs on a version after 2.4
Adorners slow down the speed of the function call. I remember.
You can't decorate the function. (You do have the knack of creating removable adorners, but no one uses them.) So once the function is decorated, it is decorated throughout the code.
The adorner wraps the function, which makes debugging difficult. (This is mitigated after the Pyhton2.5 version; see below)
Python2.5 introduces the Functools module, which contains a funtools.wraps () function that copies the decorated function name, module, and document String (docstring) to its wrapping paper.

(The interesting fact is: Funtools.wraps () or an adorner!)

# for debugging convenience, stack backtracking (stacktrace) Print function __name__ properties
def foo ():
Print "Foo"

Print foo.__name__
#输出: Foo

# When you use an adorner, it's confusing.
def bar (func):
def wrapper ():
Print "Bar"
return func ()
Return wrapper

@bar
def foo ():
Print "Foo"

Print foo.__name__
#输出: Wrapper

# "Functools" module can solve this problem

Import Functools

def bar (func):
# we said "wrapper" packed "func", and then the magic happened.
@functools. Wraps (func)
def wrapper ():
Print "Bar"
return func ()
Return wrapper

@bar
def foo ():
Print "Foo"

Print foo.__name__
#输出: Foo
How to use adorners
A big question: what can I do with the decorator?

(adorners) Look cool and powerful, but it might be better to use a practical example. There are many uses for adorners. The classic usage is to extend the functionality of an external library function (which you cannot modify) or to debug (because it is temporary and you do not want to modify it).

You can use adorners to extend multiple functions without repeating them, like this:

Def Benchmark (func):
    "" "
    Adorner for the time the print function was executed
   " ""
    Import Time
    def wrapper (*args, **kwargs):
    & nbsp;   t = time.clock ()
        res = func (*args, **kwargs)
        print func.__name__, Time.clock ()-T
         return res
    return wrapper


def logging (func):
    ""
    Recording script activity adorners
    (in fact, It is only printed, but can be printed on the log)
    ""
    def wrapper (*args, **kwargs):
  & nbsp;     res = func (*args, **kwargs)
        print Func._ _name__, args, Kwargs
        return res
    return wrapper


def counter (func):
"""
An adorner that counts and prints the number of times a function is executed
"""
def wrapper (*args, **kwargs):
Wrapper.count = Wrapper.count + 1
res = func (*args, **kwargs)
print ' {0} has been used: {1}x '. Format (func.__name__, Wrapper.count)
return res
Wrapper.count = 0
Return wrapper

@counter
@benchmark
@logging
def reverse_string (String):
Return str (Reversed (string))

Print reverse_string ("Able is I ere I saw Elba")
Print reverse_string ("A man, a plan, a canoe, pasta, heros, Rajah S, a coloratura, maps, Snipe, Percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a CRE PE, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal:panama! ")

#输出:
#reverse_string (' Able was I ere I saw Elba ',) {}
#wrapper 0.0
#wrapper has been used:1x
#ablE WA s I ere I saw ElbA
#reverse_string (' A man, a plan, a canoe, pasta, heros, Rajahs, A coloratura, maps, Snipe, Percale, Macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal:panama! ',) {}
#wrapper 0.0
#wrapper has been used:2x
#!amanap:lanac A, Noe p A, Stah Eros, Raj A, Hsac, olor A, Tur A, MapS, snip, Eperc A,) Lemac a ro (Niaga gab Ananab A, gat A, Nat a, gab Ananab A, gag A, Inoracam, Elacrep, epins, spam, arutaroloc A, Shajar, Soreh, Atsap, Eonac A, Nalp A, nam a
of course, the advantage of the adorner is that for a few All things, in the case of not rewriting, are immediately available. I said, no repetition (d.r.y, don ' t Repeat yourself):

@counter
@benchmark
@logging
Def get_random_futurama_quote ():
From Urllib import Urlopen
result = Urlopen ("Http://subfusion.net/cgi-bin/quote.pl?quote=futurama"). Read ()
Try
Value = Result.split ("<br><b>Return Value.strip ()
Except
Return "No, I ' m ... doesn ' t!"


Print Get_random_futurama_quote ()
Print Get_random_futurama_quote ()

#输出:
#get_random_futurama_quote () {}
#wrapper 0.02
#wrapper has been used:1x
#The laws of science is a Harsh mistress.
#get_random_futurama_quote () {}
#wrapper 0.01
#wrapper has been used:2x
#Curse you, merciful poseidon!
Python itself offers a number of adorners: property, Staticmethod, and so on.

Django uses adorners to manage caching (caching) and View permissions permissions.
The transformation implementation of an inline asynchronous function call.

Related Article

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.