进阶的运维开发(一)- 装饰器

2019-12-11 18:31:26 浏览数 (1)

函数在python中作为一级对象,可以被当作参数传递,也可以作为函数返回,而装饰器就是利用这一特点,装饰器首先肯定是一个高阶函数,本质即是接受函数作为参数,返回一个函数。可以在函数执行的前后做一些操作,外层函数的作用域的变量(不包括全局变量)可以被内部函数应用,感觉又是一个闭包,但装饰器就是用一个函数装饰另一个函数,用来解决相同的一类问题。

理解装饰器

不带参数的装饰器

代码语言:txt复制
import time

def timeit(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        func(*args, **kwargs)
        spend_time=time.time() - start_time
        return spend_time
    return inner

def sleep(x):
    time.sleep(x)

deco = timeit(sleep)
deco(3)
3.003096103668213

结合闭包的概念,将函数作为参数传递,将很容易理解上述过程,执行过程:timeit(sleep) -> inner(3),将sleep函数作为参数传入timeit,timeit返回inner函数,根据闭包(函数 应用环境),在外层函数的应用环境被引入inner内层函数,所以很顺利的sleep函数与inner函数就建立了连接,所以所装饰器也可以说是一个闭包。

试试语法糖:

代码语言:txt复制
import time

def timeit(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        func(*args, **kwargs)
        spend_time=time.time() - start_time
        return spend_time
    return inner

@timeit
def sleep(x):
    time.sleep(x)

sleep(3)
3.0008034706115723

整个引用过程就更简单了,可以理解:sleep=timeit(sleep)

带参数的装饰器

代码语言:txt复制
import time

def timeit(offset=0):
    def outer(func):
        def inner(*args, **kwargs):
            start_time = time.time()
            func(*args, **kwargs)
            spend_time=time.time() - start_time   offset
            return spend_time
        return inner
    return outer

def sleep(x):
    time.sleep(x)

deco = timeit(1)
deco1 = deco(sleep)
deco1(3)
4.0014448165893555

根据不带参数的装饰器的理解,这个函数也是很好理解的。

试试语法糖:

代码语言:txt复制
import time

def timeit(offset=0):
    def outer(func):
        def inner(*args, **kwargs):
            start_time = time.time()
            func(*args, **kwargs)
            spend_time=time.time() - start_time   offset
            return spend_time
        return inner
    return outer

@timeit(1)
def sleep(x):
    time.sleep(x)

sleep(3)
4.0018510818481445

执行过程:timeit(1) -> outer(sleep) -> inner(3),其实说到这,装饰器就就是更多层的闭包。

多个装饰器叠加

首先我们要查看多个迭代器的的执行过程

代码语言:txt复制
def deco_1(func):
    print('enter deco_1')
    def inner1(*args, **kwargs):
        print('enter deco_1_inner')
        print(func.__name__)
        return func(*args, **kwargs)
    return inner1

def deco_2(func):
    print('enter deco_2')
    def inner2(*args, **kwargs):
        print('enter deco_2_inner')
        print(func.__name__)
        return func(*args, **kwargs)
    return inner2

@deco_1
@deco_2
def Print(*args, **kwargs):
    return args
enter deco_2
enter deco_1

Print(1, 2)
enter deco_1_inner
inner2
enter deco_2_inner
Print

(1, 2)

从这个例子中可以看出:Print(1, 2)=deco_1(deco_2(Print(1, 2)))

整个执行过程从下往上执行,注意看下输出的函数名的变化

不明白叠加装饰器的的使用场景,结合下面的使用场景看看:

代码语言:txt复制
import sys

def checkpass(func):
    def inner(*args, **kwargs):
        if pass_database.get(user, None) == password:
            func(*args, **kwargs)
        else:
            print('Password is not correct!')
            sys.exit(2)
    return inner

def checkuser(func):
    def inner(*args, **kwargs):
        if user in pass_database:
            func(*args, **kwargs)
        else:
            print('User is not presenet')
            sys.exit(3)
    return inner

@checkuser
@checkpass
def index(user, password):
    print("Hello {}!".format(user))

pass_database = {"dev": "1234567"}

user=input("请输入user:")
password=input("请输入pass:")
index(user, password)

类装饰器

使用类装饰器,是依靠类的魔术方法call实现,当使用@形式将装饰器附加到函数的时候就会调用此方法:

代码语言:txt复制
class deco:
    def __init__(self, func):
        self._func = func

    def __call__(self):
        print('enter')
        self._func()
        print('exit')

@deco
def Print():
    print(123)

保留原函数的信息

代码语言:txt复制
import time

def timeit(func):
	def inner(*args,**kwargs):
		start_time=time.time()
		func(*args,**kwargs)
		spend_time=time.time()-start_time
		return spend_time
	return inner

@timeit
def sleep(x):
	''' sleep '''
	time.sleep(x)

print(sleep.__name__,sleep.__doc__)

inner None

通过装饰器一装饰,原本sleep的函数信息就全部没了,成了一个新函数,而为了避免这种情况,functools中wraps可以将原本的函数属性全部更新到新函数中。

代码语言:txt复制
from functools import wraps
import time

def timeit(func):
	@wraps(func)
	def inner(*args,**kwargs):
		start_time=time.time()
		func(*args,**kwargs)
		spend_time=time.time()-start_time
		return spend_time
	return inner

@timeit
def sleep(x):
	''' sleep '''
	time.sleep(x)

print(sleep.__name__,sleep.__doc__)

sleep  sleep

以梦为马 不负韶华 归来仍是少年

0 人点赞