[Advanced] 1-4 decorator python decorator | write no parameters decorator / write parameters decorator / decorator perfect


Decorator

@ python built-in grammar will make the call in order to simplify decorator

Action decorators:
greatly simplify the code written for each function to avoid repetitive coding
print log: @log
detection performance: @performance
database transaction: @Transaction
the URL Routing: @post ( '/ register')

1, python in writing no parameters decorator

The decorator nature of Python is a higher-order function that accepts a function as a parameter, and then returns a new function.

Use @ decorator syntax provided in Python, to avoid hand written f = decorate (f) such code.

Consider a @log definition:

def log(f):
    def fn(x):
        print 'call ' + f.__name__ + '()...'
        return f(x)
    return fn

For the factorial function, @ log work well:

@log
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))
print factorial(10)

result:

call factorial()...
3628800

However, the argument is not a function of the call will be given:

@log
def add(x, y):
    return x + y
print add(1, 2)

result:

Traceback (most recent call last):
  File "test.py", line 15, in <module>
    print add(1,2)
TypeError: fn() takes exactly 1 argument (2 given)

Because the add () function takes two arguments, but @log write a function that returns the dead with only one parameter.

@Log make any adaptive function defined parameters may be utilized in the Python * args and ** kw, arbitrary number of arguments always ensure normal call:

def log(f):
    def fn(*args, **kw):
        print 'call ' + f.__name__ + '()...'
        return f(*args, **kw)
    return fn

Now, for any function, @ log can work properly.

task:

Please write a @performance, it can print out the time function calls.

CODE:

Calculating a time function call may be recorded before and after the timestamp of the current call, and then calculates the difference between two timestamps.

import time
def performance(f):
    def fn(*args, **kw):
        t1 = time.time()
        r = f(*args, **kw)
        t2 = time.time()
        print 'call %s() in %fs' % (f.__name__, (t2 - t1))
        return r
    return fn

@performance
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))
print factorial(10)


2, python in written arguments decorator

A study on the @logDecorator:

def log(f):
    def fn(x):
        print 'call ' + f.__name__ + '()...'
        return f(x)
    return fn

For decorative function is found, the statement print log can not change (except the function name).

If some function is very important, I want to print out '[INFO] call xxx()...', and some less important function, print out the hope that '[DEBUG] call xxx()...', this time, log function itself will need to pass 'INFO'or 'DEBUG'such a parameter, like this:

@log('DEBUG')
def my_func():
    pass

To translate the above definition to invoke higher-order functions, that is:

my_func = log('DEBUG')(my_func)

The above statement seems quite around, then expand it:

log_decorator = log('DEBUG')
my_func = log_decorator(my_func)

The above statement is equivalent to another:

log_decorator = log('DEBUG')
@log_decorator
def my_func():
    pass

Therefore, the first log function with parameters a decorator function returns, let the decorator my_func function receives and returns the new function:

def log(prefix):
    def log_decorator(f):
        def wrapper(*args, **kw):
            print '[%s] %s()...' % (prefix, f.__name__)
            return f(*args, **kw)
        return wrapper
    return log_decorator

@log('DEBUG')
def test():
    pass
print test()

Results of the:

[DEBUG] test()...
None

For this three-layer nested decorator defined, you can put it apart:

# 标准decorator:
def log_decorator(f):
    def wrapper(*args, **kw):
        print '[%s] %s()...' % (prefix, f.__name__)
        return f(*args, **kw)
    return wrapper
return log_decorator

# 返回decorator:
def log(prefix):
    return log_decorator(f)

Will open later found, the call will fail, because the definition of the decorator nesting 3, the innermost layer of the outermost wrapper references prefix parameter, so that the closure is split into a regular function call can be difficult . It does not support the closure of the programming language to achieve the same function requires more code.

task:

The previous section @performancecan only print seconds, please @performaceadd a parameter to allow incoming 's' or 'ms':

@performance('ms')
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))

CODE:

import time
def performance(unit):
    def perf_decorator(f):
        def wrapper(*args, **kw):
            t1 = time.time()
            r = f(*args, **kw)
            t2 = time.time()
            t = (t2 - t1) * 1000 if unit=='ms' else (t2 - t1)
            print 'call %s() in %f %s' % (f.__name__, t, unit)
            return r
        return wrapper
    return perf_decorator

@performance('ms')
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))
print factorial(10)

Here Insert Picture Description

3, python decorator perfect in

@decorator can dynamically increase the functional capabilities to achieve, however, through the function, and the original function after @decorator "reform" compared to, in addition to function a little more, but there is no other different places?

In the absence decorator, the printing function name:

def f1(x):
    pass
print f1.__name__

Output: f1

There decorator under the circumstances, and then print the function name:

def log(f):
    def wrapper(*args, **kw):
        print 'call...'
        return f(*args, **kw)
    return wrapper
@log
def f2(x):
    pass
print f2.__name__

Output: wrapper

Visible, because the decorator function returns a new function name is not 'f2', but @log internally defined 'wrapper'. This will fail to code-dependent function names. Other properties are also changed decorator __doc__ like function. If you want the caller can not see through the @decorator of "reform" a function, you need to copy some of the attributes of the original function to a new function:

def log(f):
    def wrapper(*args, **kw):
        print 'call...'
        return f(*args, **kw)
    wrapper.__name__ = f.__name__
    wrapper.__doc__ = f.__doc__
    return wrapper

Write decorator is very convenient, because we can hardly put all the necessary attributes are a function of the original copied onto a new function, the built-in Python functools can be used to automate the complete "copy" of tasks:

import functools
def log(f):
    @functools.wraps(f)
    def wrapper(*args, **kw):
        print 'call...'
        return f(*args, **kw)
    return wrapper

Finally it should be noted, because we changed the original function signature (* args, ** kw), therefore, can not get the original parameter information of the original function. Even if we use fixed parameters to decorate the function of only one parameter:

def log(f):
    @functools.wraps(f)
    def wrapper(x):
        print 'call...'
        return f(x)
    return wrapper

It may change the original function of the parameter name because the parameter name of the new function is always 'x', the original function defined parameter names are not necessarily called 'x'.

task:

Consider the arguments @ decorator, @ functools.wraps should be placed in which:

def performance(unit):
    def perf_decorator(f):
        def wrapper(*args, **kw):
            ???
        return wrapper
    return perf_decorator

CODE:

import time, functools
def performance(unit):
    def perf_decorator(f):
        @functools.wraps(f)
        def wrapper(*args, **kw):
            t1 = time.time()
            r = f(*args, **kw)
            t2 = time.time()
            t = (t2 - t1) * 1000 if unit=='ms' else (t2 - t1)
            print 'call %s() in %f %s' % (f.__name__, t, unit)
            return r
        return wrapper
    return perf_decorator

@performance('ms')
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))
print factorial.__name__
Published 20 original articles · won praise 0 · Views 397

Guess you like

Origin blog.csdn.net/yipyuenkay/article/details/104253829