专栏首页运维杂记进阶的运维开发(一)- 装饰器
原创

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

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

理解装饰器

不带参数的装饰器

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函数就建立了连接,所以所装饰器也可以说是一个闭包。

试试语法糖:

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)

带参数的装饰器

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

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

试试语法糖:

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),其实说到这,装饰器就就是更多层的闭包。

多个装饰器叠加

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

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)))

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

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

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实现,当使用@形式将装饰器附加到函数的时候就会调用此方法:

class deco:
    def __init__(self, func):
        self._func = func

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

@deco
def Print():
    print(123)

保留原函数的信息

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可以将原本的函数属性全部更新到新函数中。

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

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

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 进阶的运维开发(三)- 反射

    反射就是通过字符串的形式,导入模块,通过字符串的形式,去模块寻找制定函数并执行。利用字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员,一种基于字符...

    奔跑的骆驼
  • 进阶的运维开发(二)- 迭代器和生成器

    python迭代器于平常的可迭代对象相比,拥有占用字节少等优点,往往在处理大量可迭代对象的时候应该优先考虑迭代器实现,如下面的例子:

    奔跑的骆驼
  • shell程序计时

    有时候写了一个pipeline,我们想知道具体会跑多长时间,这就需要实现计时功能,可以用date或者time实现。

    生信编程日常
  • 103-多进程的效率

    没有多进程,即使CPU有多个核心,程序只是运行在一个核心上,无法利用多进程提升效率。5000万次加法,如果需要2.5秒,调用两次共花费5秒。

    凯茜的老爸
  • python获取文件修改时间与创建时间

    转载自:  http://blog.csdn.net/liyuan_669/article/details/25347037

    py3study
  • python笔记7-多线程threading

    前言 1.python环境2.7 2.threading模块系统自带 一、 单线程 1.平常写的代码都是按顺序挨个执行的,就好比吃火锅和哼小曲这两个行为事件,定...

    上海-悠悠
  • c++那些事儿4.0 多态

    ---- 相关知识点 多态: 向不同对象发送一个消息,不同的对象接收会产生不同的行为。 a.静态多态性。 函数重载,最经典就是构造函数重载...

    东风冷雪
  • ASP.NET Core Blazor 初探之 Blazor WebAssembly

    最近Blazor热度很高,传说马上就要发布正式版了,做为微软脑残粉,赶紧也来凑个热闹,学习一下。

    kklldog
  • 数据库系统——B+树索引

    http://blog.csdn.net/cjfeii/article/details/10858721

    bear_fish
  • Android开发笔记(一百四十八)自定义输入法软键盘

    手机上输入文字,都是通过系统自带的软键盘,这个软键盘可以是Android自带的,也可以是第三方软键盘如搜狗输入法。多数情况下面...

    用户4464237

扫码关注云+社区

领取腾讯云代金券