程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Decorator and its principle in Python

編輯:Python

1. introduction

be familiar with Java Your programmers must be right Java To understand the powerful annotations in ,Python To a certain extent, it has been affected Java Influence , The birth of Python Decorator characteristics of . Python The decorator is a very powerful function , In this article, we will introduce in detail Python Decorator characteristics of . For example, the well-known class static methods are often used in the definition @staticmethod And @classmethod Decorator :

class TestClass:
@staticmethod
def staticfoo():
pass
@classmethod
def clsfoo(cls):
pass
if __name__ == '__main__':
Test.staticfoo()
Test.clsfoo()

2. Decorator

Decorators are callable objects , Usually used to enhance or replace functions ,Python Class decorators are also supported .

def decorator(func):
func()
print('this is decorator')
@decorate
def target():
print('running target()')

2.1. The principle of decorator

Essentially , The above example has the same effect as the following :

def decorator(func):
func()
print('this is decorator')
def target():
print('running target()')
target = decorate(target)

3. The timing of decorator execution

registry = []
def register(func):
print('running register(%s)' % func)
registry.append(func)
return func
@register
def f1():
print('running f1()')
@register
def f2():
print('running f2()')
def f3():
print('running f3()')
def main():
print('running main()')
print('registry ->', registry)
f1()
f2()
f3()
if __name__=='__main__':
main()

Execution procedure , Printed out :

>>> python3 registration.py running register(<function f1 at 0x100631bf8>) running register(<function f2 at 0x100631c80>) running main() registry -> [<function f1 at 0x100631bf8>, <function f2 at 0x100631c80>] running f1() running f2() running f3() >>> import registration running register(<function f1 at 0x10063b1e0>) running register(<function f2 at 0x10063b268>)

The example above shows , Decorators are executed immediately when a module is imported , Decorated functions only run when explicitly called .

4. Closures and decorators

We see that when a module is imported , The code in the decorator is executed , Usually we want to enhance the decorated method when it is executed , So we obviously don't want the decorator code to execute at the time of module import . We can solve this problem through the closure example above .

def decorator(func):
def restructure():
func()
print('this is decorator')
return restructure
@decorator
def target():
print('this is target')

In the above example , When the module is loaded , The decorator is executed , therefore restructure Methods are defined , And because of the nature of closures ,restructure Free variables can be accessed internally func, So as to carry out the func Enhanced code for .

5. Implement decorator mode through decorator

5.1. Decorator mode

In the previous article, we introduced the decorator pattern :

Specific in decorator mode Decorator The implementation class forwards the request for the build to the decorated object , And perform some additional actions before and after forwarding to modify some of the original behavior , Implementation enhancements Component The purpose of the requested method in the object . Decorator mode is a very flexible , Design patterns that can dynamically add and separate additional operations ,python The decorator in is named for this pattern , It is also a powerful tool to realize this design pattern .

5.2. python The decorator realizes automatic monitoring

A typical application scenario of decorator mode is to realize non discriminatory automatic log printing and some statistical functions of monitoring and reporting for all methods that need to be monitored . The following example shows the automatic printing of function execution time :

import time
import functiontools
import logging
def clock(func):
@functools.wraps(func)
def clocked(*args):
t0 = time.perf_counter()
result = func(*args)
elapsed = time.perf_counter() - t0
name = func.__name__
arg_str = ', '.join(repr(arg) for arg in args)
logging.info('[%0.8fs] %s(%s) -> %r' % (elapsed, name, arg_str, result))
return result
return clocked

In the example above , Through decorators and closures , Realized with func The enhancement of , Through ornaments clock, Automatically in log The execution time of the method is printed in . We define functions :

@clock
def snooze(seconds):
time.sleep(seconds)
return seconds
if __name__ == '__main__':
print(snooze(.123))

Printed out :

[0.12405610s] snooze(.123) -> 0.123 0.123

5.2.1. functools.wraps

functools.wraps Is a decorator in the standard library , He changed the relevant attributes from func Copied to the clocked in , Thus, the external performance of the decorator and the performance of the decorated function are always .

5.3. Monitoring Optimization — Add parameters

A lot of times , We need to pass some parameters on the decorator , To achieve customized requirements for different scenarios , For example, sometimes we want to print out all the parameters 、 Return value , Sometimes you need to hide exceptions when they occur , Return to preset default values, etc . After knowing the principle of the decorator , Decorators with parameters are easy to write , in fact , No matter how many layers are nested , Just remember the principle of the decorator we mentioned at the beginning , It is not difficult to understand .

import inspect
import logging
import time
import uuid
from functools import wraps
def report(project=None, type=None, name=None, raise_exception=True, result=None, paramslog=False, returnlog=False):
def decorator(func):
@wraps(func)
def wrapper(*arg, **kvargs):
cattype = type
if cattype is None:
cattype = inspect.getfile(func)
if project is not None:
cattype = '[:: ' + project + ' ::] ' + cattype
catname = name if name is not None else func.__name__
randid = str(uuid.uuid1())
if paramslog:
logging.info('<%s::%s> [%s] PARAMS: %r; %r' % (cattype, catname, randid, arg, kvargs))
try:
t0 = time.perf_counter()
res = func(*arg, **kvargs)
elapsed = time.perf_counter() - t0
if returnlog:
logging.info('<%s::%s> [%s;%0.8fs] RESULT: %r' % (cattype, catname, randid, elapsed, res))
return res
except Exception as e:
logging.error('<%s::%s> [%s] ERROR: %r' % (cattype, catname, randid, e), exc_info=True)
if raise_exception:
raise e
else:
return result
return wrapper
return decorator

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved