Decorators in Python are your gateway to Python, and they’re there whether you cross them or not.

Let’s assume that your program implements the say_hello() and say_goodbye() functions.

def say_hello():

print "hello!"

def say_goodbye():

print "hello!"  # bug here

if name == ‘__main__’:

say_hello()
say_goodbye()

But in the actual call, we find that there is an error. The above code prints two Hellos. After debugging, you find that say_goodbye() is wrong. The boss requires that the name of the entry function be recorded before calling each method, like this:

Hello!

Goodbye! Okay, so Little A is A graduate, and here’s how he did it.

def say_hello():

print "[DEBUG]: enter say_hello()"
print "hello!"

def say_goodbye():

print "[DEBUG]: enter say_goodbye()"
print "hello!"

if name == ‘__main__’:

say_hello()
say_goodbye()

Very low? Well, yes. Little B has been working for A while, and he tells little A to write like this.

def debug():

import inspect
caller_name = inspect.stack()[1][3]
print "[DEBUG]: enter {}()".format(caller_name)   

def say_hello():

debug()
print "hello!"

def say_goodbye():

debug()
print "goodbye!"

if name == ‘__main__’:

say_hello()
say_goodbye()

Is it better? Sure, but wouldn’t it be hard to call debug() in every business function? What if your boss says you don’t need to debug “say” but “do”?

This is where the decorator comes in.

A decorator is essentially a Python function that allows other functions to add extra functionality without making any code changes. The return value of the decorator is also a function object. It is often used in scenarios where aspects are required, such as insertion logging, performance testing, transaction processing, caching, permission verification, and so on. Decorators are a great way to solve this problem, because they allow us to extract and reuse a lot of similar code that has nothing to do with the function itself. The front row is 762, the middle row is 459, and the back row is 510. Put the above three groups of letters together in order. She will arrange to learn.

In a nutshell, decorators add additional functionality to an existing function or object.

In the old days (Python Version < 2.4, pre-2004), adding extra functionality to a function was written like this.

def debug(func):

def wrapper():
    print "[DEBUG]: enter {}()".format(func.__name__)
    return func()
return wrapper

def say_hello():

print "hello!"

Say_hello = debug(say_hello) # Add functionality and keep the name of the original function unchanged. The above debug function is already a decorator, it wraps the original function and returns another function, adding some extra functionality. Since this is less elegant, @syntactic sugar is supported in later versions of Python, and the following code is equivalent to the earlier writing. The front row is 762, the middle row is 459, and the back row is 510. Put the above three groups of letters together in order. She will arrange to learn.

def debug(func):

def wrapper():
    print "[DEBUG]: enter {}()".format(func.__name__)
    return func()
return wrapper

@debug

def say_hello():

print "hello!"

This is the simplest decorator, but there is a problem. If the function being decorated needs to pass arguments, then the decorator is broken. Since the returned functions do not take arguments, you can specify that the wrapper decorator functions take the same arguments as the original functions. For example:

def debug(func):

Def wrapper(something): print "[DEBUG]: Enter {}()".format(func.__name__) return func(something) return wrapper #

@debug

def say(something):

print "hello {}!" .format(something)

So you’ve solved one problem, but you’ve added N more problems. Because there are thousands of functions out there, and you just focus on your own function, and who knows what other people’s arguments look like? Fortunately, Python provides the variable argument args and the keyword argument *kwargs, with which decorators can be used for any target function.

def debug(func):

Print "[DEBUG]: enter {}()". Format (func.__name__) print 'Prepare and say... Return func(*args, **kwargs) return wrapper #

@debug

def say(something):

print "hello {}!" .format(something)

At this point, you have mastered the rudiments of decorator writing.

Advanced decorators Parameter decorators and class decorators are advanced content. Before understanding these decorators, it’s a good idea to have some knowledge of function closures and decorator interface conventions. (See Python closures)

Assuming that our previous decorator needs to do more than just print log information when entering a function, but also specify the log level, the decorator will look something like this.

def logging(level):

def wrapper(func):
    def inner_wrapper(*args, **kwargs):
        print "[{level}]: enter function {func}()".format(
            level=level,
            func=func.__name__)
        return func(*args, **kwargs)
    return inner_wrapper
return wrapper

@logging(level=’INFO’)

def say(something):

print "say {}!" .format(something)

If you don’t use the @ syntax, it equals

say = logging(level=’INFO’)(say)

@logging(level=’DEBUG’)

def do(something):

print "do {}..." .format(something)

if name == ‘__main__’:

say('hello')
do("my work")

Are you feeling a little dizzy? You can understand that when an argument decorator is slapped on a function, such as @logging(level=’DEBUG’), which is actually a function, it will be executed immediately. As long as it returns a decorator, that’s fine. Feel it again.

A decorator function is an interface constraint that it must take a Callable object as an argument and then return a Callable object. In Python, callable objects are usually functions, but there are exceptions. An object is callable as long as it overloads __call__().

class Test():

def __call__(self): print 'call me! '

T = Test() t() # call me Methods like __call__, which are underlined before and after, are called built-in methods in Python, and are sometimes called magic methods. Overloading these magic methods generally changes the internal behavior of the object. The example above allows a class object to have the behavior of being called.

Returning to the concept of decorators, decorators are required to accept a Callable object and return a Callable object (less rigorously, more later). It’s also possible to implement it with a class. We can have the class constructor __init__() accept a function, then overload __call__() and return a function, or we can have the effect of a decorator function. The front row is 762, the middle row is 459, and the back row is 510. Put the above three groups of letters together in order. She will arrange to learn.

class logging(object):

def __init__(self, func):
    self.func = func

def __call__(self, *args, **kwargs):
    print "[DEBUG]: enter function {func}()".format(
        func=self.func.__name__)
    return self.func(*args, **kwargs)

@logging

def say(something):

print "say {}!" .format(something)

If you need to implement the parameterized decorator as a class, it’s a little more complicated than the previous examples. Instead of accepting a function in the constructor, the argument is passed in. These parameters are saved by the class. Then overloading __call__ requires accepting a function and returning a function.

class logging(object):

Def __init__(self, level='INFO'): self.level = level def call__(self, func): print "[{level}]: Enter function {func}()".format(level=self.level, func=func.__name__) func(*args, **kwargs) return wrapper #

@logging(level=’INFO’)

def say(something):

print "say {}!" .format(something)

Built-in decorators work just like regular decorators, except they return class objects instead of functions, so they’re a little harder to understand.

At sign property Before you can understand this decorator, you need to know how to write a property without using decorators.

def getx(self):

return self._x

def setx(self, value):

self._x = value

def delx(self):

del self._x

create a property

X = property(getx, setx, delx, “I am doc for x property”) With @ grammar sugar, you get the same effect but it looks a lot easier.

@property

def x(self): …

Is equivalent to

def x(self): … The x = property(x) property has three decorators: The setter, getter, and deleter are all encapsulated in a property(), because the setter and deleter are the second and third arguments to the property(), so we can’t use the @ syntax directly. Getter decorators have the same effect as property decorators without getters. It’s probably just for rounding up numbers, and it doesn’t have any meaning in itself. A function decorated with @Property returns a property object instead of a function.

property()


<property object at 0x10ff07940>


@ staticmethod, @ classmethod


With the @ Property decorator in mind, the two decorators work in much the same way. @StaticMethod returns a StaticMethod object, and @ClassMethod returns a ClassMethod object. They all call their own __init__() constructor.

class classmethod(object):

"""
classmethod(function) -> method
"""    
def __init__(self, function): # for @classmethod decorator
    pass
# ...

class staticmethod(object):

"""
staticmethod(function) -> method
"""
def __init__(self, function): # for @staticmethod decorator
    pass
# ...

The @ syntax of the decorator is equivalent to calling the constructors of both classes.

class Foo(object):

@staticMethod def bar(): pass = bar = staticMethod ()

The decorator must accept a Callable object. It doesn’t really care what you return. It can be another Callable object (in most cases), or it can be another class object, such as a property. The front row is 762, the middle row is 459, and the back row is 510. Put the above three groups of letters together in order. She will arrange to learn.

Those pit decorators in decorators can make your code more elegant and less repetitive, but they are not all good and can cause some problems.

Mislocated code Let’s look directly at the sample code.

def html_tags(tag_name):

print 'begin outer function.'
def wrapper_(func):
    print "begin of inner wrapper function."
    def wrapper(*args, **kwargs):
        content = func(*args, **kwargs)
        print "<{tag}>{content}</{tag}>".format(tag=tag_name, content=content)
    print 'end of inner wrapper function.'
    return wrapper
print 'end of outer function'
return wrapper_

@html_tags(‘b’)

def hello(name=’Toby’):

return 'Hello {}! '.format(name)

Hello () hello() in the decorator I’ve added print statements in every possible place to record what was called. Do you know what order they finally printed it out? If you are unsure, it is best not to add logic to the decorator functions, otherwise the decorator will be out of your control. Here is the output:

begin outer function. end of outer function begin of inner wrapper function. end of inner wrapper function. Hello Toby! Hello Toby! Error function signature and document decorator decorated functions that appear to have the same name have changed.

def logging(func):

def wrapper(*args, **kwargs):
    """print log before a function."""
    print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)
    return func(*args, **kwargs)
return wrapper

@logging

def say(something):

"""say something""" print "say {}!" .format(something)

Print say.__name__ # wrapper Just think of the syntax for decorators: sugar @ instead of something. At sign is the same thing as writing it like this.

Say = logging(say) logging returns the name of the function inside the wrapper. The name of say is __name__. The name of say is __name__ inside the wrapper. Other properties are also from the wrapper, such as doc, source, and so on.

Using functools.wraps from the standard library, you can basically solve this problem.

from functools import wraps

def logging(func):

@wraps(func)
def wrapper(*args, **kwargs):
    """print log before a function."""
    print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)
    return func(*args, **kwargs)
return wrapper

@logging

def say(something):

"""say something""" print "say {}!" .format(something)

Print say.__name__ # say print say.__doc__ # say something looks good! The main problem is solved, but it’s not perfect. Because the function signature and source code is still not available.

Import inspect print inspect.getargSpec (say) # failed print inspect.getSource (say) # failed There is an introduction after the article.

You can’t decorate @staticmethod or @classmethod. So when you try to use a decorator on a staticmethod or a classmethod, sorry, it’s an error.

class Car(object):

Def __init__(self, model): self.model = model@logging, OK def run(self): print "{} is running!" Def check_model_for(obj): if isinstance(obj, Car): print "The model of your car is {}".format(obj.model) else: print "{} is not a car!" .format(obj)

“””

Traceback (most recent call last):



File “example_4.py”, line 10, in logging

@wraps(func)

File “C:\Python27\lib\functools.py”, line 33, in update_wrapper

setattr(wrapper, attr, getattr(wrapped, attr))

AttributeError: ‘StaticMethod’ object has no attribute ‘__module__’ “”” “@staticMethod does not return a Callable object, If it’s a StaticMethod object, then it doesn’t meet decorator requirements (such as passing in a Callable object), so you can’t add another decorator to it. To solve this problem, simply place your decorator before @StaticMethod, since your decorator will return a normal function, and then add @StaticMethod.

class Car(object):

Def __init__(self, model): self. Model = model @staticmethod @logging, OK def check_model_for(obj): pass

How to optimize your decorators Nesting decorators is not very intuitive, we can use third-party package classes to improve this situation and make the decorator functions more readable.

Decorator.py is a very simple decorator enhancement package. You can intuitively complete a decorator by defining the wrapper() function and then using the classpath (func, wrapper) method.

from decorator import decorate

def wrapper(func, args, *kwargs):

"""print log before a function."""
print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)
return func(*args, **kwargs)

def logging(func):

Return classpath (classpath) # decorate func with a wrapper

You can also use its built-in @decorator to complete your decorator.

from decorator import decorator

@decorator

def logging(func, args, *kwargs):

print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)
return func(*args, **kwargs)

The decorator.py implementation preserves the name, doc, and args of the original function. The only problem is that inspect.getSource (func) still returns the source code of the decorator. You need to change it to inspect.getSource (func.__wrapped__).

Wrapt wrapt is a very functional package for implementing all kinds of decorators that you might or might not have thought of. With the wrapt implementation you don’t need to worry about all the problems you encountered with Inspect, because it does it for you. Even inspect.getSource (func) is correct.

import wrapt

without argument in decorator

@wrapt.decorator

def logging(wrapped, instance, args, kwargs): # instance is must

print "[DEBUG]: enter {}()".format(wrapped.__name__)
return wrapped(*args, **kwargs)

@logging def say(something): Wrapped, instance, args, kwargs). Note that the second parameter instance is required even if you don’t use it. Wrapped, instance, args, kwargs. It’s going to get different values when it decorates different places, like when it decorates a class instance method you can get that class instance. You can adjust your decorator more flexibly depending on the instance value. Also, args and kwargs are fixed. Note that there is no asterisk in front of them. The original function is called within the decorator with an asterisk. The front row is 762, the middle row is 459, and the back row is 510. Put the above three groups of letters together in order. She will arrange to learn.

If you need to use wrapt to write a decorator with arguments, do so.

def logging(level):

@wrapt.decorator
def wrapper(wrapped, instance, args, kwargs):
    print "[{}]: enter {}()".format(level, wrapped.__name__)
    return wrapped(*args, **kwargs)
return wrapper

@logging(level=”INFO”) def do(work): pass

Python decorators are not the same as Java annotations, nor are they the same as C# attributes.

The idea of a decorator is that it is an enhancement of the original function or object, equivalent to repackaging, so the common decorator function is named wrapper(), meaning wrapper. A function is only useful when it is called. For example, @logging decorator can output additional logs when a function is executed, @Cache decorator can cache results, and so on.

Annotations and features add attributes to target functions or objects, classifying them. These properties can be obtained by reflection, which can be used to interfere with different properties or objects at runtime. For example, a function with Setup is executed as a preparation step, or all functions with TestMethod are found and executed in turn, and so on. The front row is 762, the middle row is 459, and the back row is 510. Put the above three groups of letters together in order. She will arrange to learn.

So far I’ve covered everything I know about decorators, but there are some things I haven’t covered, such as decorators for the decorator class. I’ll add to that when I get a chance. Thanks for watching. If you do meet a good colleague, you’ll be lucky. Go ahead and learn. Python, PythonWeb, crawler, data analysis and other Python skills, as well as learning methods of artificial intelligence, big data, data mining, office automation and so on. Build from zero foundation to project development start combat all-round analysis!