Python 装饰器

装饰器是一种函数的函数,需要传入函数或类作为参数。让其他函数在不需要做任何代码变动的前提下,进而通过实现各种功能来对这个函数的功能进行增强。

应用场景:

装饰器最大的优势是用于解决重复性的操作,比如计算函数运行时间,给函数打日志等

以下的例子实现了计算一个函数的运行时间

import time
def func(n):
    time.sleep(1)
    print(n,"This is function func!")

start=time.time()
func(1)
print(time.time()-start)
1 This is function func!
1.0037739276885986

如果有多个函数需要计算,代码量将会重复和复杂。此时可以定义一个函数帮助实现该功能。

def run_time(func):
    def wrapper(*args, **kwargs):#wrapper包装纸,封皮的意思
        start = time.time()
        func(*args, **kwargs)
        end = time.time()
        cost_time = end - start
        print(cost_time)
    return wrapper
test1=run_time(func)
test1(1)
test2=run_time(func)
test2(3)
1 This is function func!
1.0043902397155762
3 This is function func!
1.0031471252441406

更简单的写法,可以用装饰器@符号

@run_time
def func(n):
    time.sleep(1)
    print(n,"This is function func!")
func(1)
func(3)
func('hello')
1 This is function func!
1.0044219493865967
3 This is function func!
1.0023789405822754
hello This is function func!
1.004396915435791

带参数的装饰器

如果装饰器本身需要传入参数,则需要在原来的函数上,再嵌套一层。

import logging

def run_time(info=None):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if info != None:
                print(info)
            return func(*args)
        return wrapper
    return decorator


@run_time("hello")
def func(n):
    time.sleep(1)
    print(n,"This is function func!")
func(7)
func(9)
hello
7 This is function func!
hello
9 This is function func!

类装饰器

除了函数,python的类也可以被装饰
举几个例子:

def singleton(cls, *args, **kw):
    instances = {}

    def _singleton():
        if cls not in instances:
            instances[cls] = cls(*args, **kw)
        return instances[cls]

    return _singleton


@singleton
class A():
    pass
#主类会做成单例(singleton)以避免重复初始化
class Foo(object):
    def __init__(self, func):
        self._func = func

    def __call__(self):
        print ('class decorator runing')
        self._func()
        print ('class decorator ending')

@Foo
def bar():
    print ('bar')

bar()
class decorator runing
bar
class decorator ending

functools.wraps

实现将原函数的元信息(namedoc等)拷贝到装饰器函数中,不添加此装饰器,会丢失元信息
举例:

import time
def func(n):
    '''This is __doc__ of func'''
    time.sleep(1)
    print(n,"This is function func!")


print(func.__name__)
print(func.__doc__)
func This is __doc__ of func
def run_time(func):
    def wrapper(*args, **kwargs):#wrapper包装纸,封皮的意思
        start = time.time()
        func(*args, **kwargs)
        end = time.time()
        cost_time = end - start
        print(cost_time)
    return wrapper

@run_time
def func(n):
    '''This is __doc__ of func'''
    time.sleep(1)
    print(n,"This is function func!")

print(func.__name__)
print(func.__doc__)
wrapper
None

添加functools.wraps装饰器后

from functools import wraps
def run_time(func):
    @wraps(func)
    def wrapper(*args, **kwargs):#wrapper包装纸,封皮的意思
        start = time.time()
        func(*args, **kwargs)
        end = time.time()
        cost_time = end - start
        print(cost_time)
    return wrapper

@run_time
def func(n):
    '''This is __doc__ of func'''
    time.sleep(1)
    print(n,"This is function func!")

print(func.__name__)
print(func.__doc__)
func
This is __doc__ of func

总结

@是python的语法糖,类似于@后面的东西(类或者函数)以调用的函数为参数,实现某些重复有效的功能。


Categories: Python

0 Comments

Leave a Reply

Your email address will not be published.