前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python基础09-装饰器

Python基础09-装饰器

作者头像
DriverZeng
发布2022-09-26 11:33:13
2500
发布2022-09-26 11:33:13
举报
文章被收录于专栏:Linux云计算及前后端开发

-曾老湿, 江湖人称曾老大。


-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。


装饰器介绍


什么是装饰器

装饰器,就是用来为被装饰器对象,添加新功能的工具

代码语言:javascript
复制
装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。
强调装饰器的原则:1 不修改被装饰对象的源代码 2 不修改被装饰对象的调用方式
装饰器的目标:在遵循1和2的前提下,为被装饰对象添加上新功能

为什么要用装饰器

开放封闭原则:对修改封闭,对扩展开放

装饰器的实现,必须遵循两大原则。 1.不修改被装饰对象的源代码 2.不修改被装饰对象的调用方式

装饰器的目标,就是在遵循1和2原则的前提下为被装饰对象添加上新功能


装饰器简单版

代码语言:javascript
复制
# 模拟网站响应
import time

def index():
    print('welcome to index page')
    time.sleep(3)

# 需求,给上面的index函数,加一个新功能,统计他的执行时间
import time

def index():
    print('welcome to index page')
    time.sleep(3)


start=time.time()
index()
stop=time.time()
print('Run Time Is: %s' %(stop - start))

# 是不是满足了两大原则?
# 1.没有改变被装饰对象的源代码
# 2.没有改变被装饰对象的调用方式

# 哦~~~~这原来就是装饰器啊...
# 装饰你妹啊~

# 想一个问题,如果我们有login函数,register函数...等1万个函数,改怎么写?

start=time.time()
index()
stop=time.time()
print('Run Time Is: %s' %(stop - start))

start=time.time()
register()
stop=time.time()
print('Run Time Is: %s' %(stop - start))

start=time.time()
login()
stop=time.time()
print('Run Time Is: %s' %(stop - start))

# 智障...全都是重复代码,可以去屎了,那么我们来优化一下代码
import time

def index():
    print('welcome to index page')
    time.sleep(3)


def wrapper(func):
    start=time.time()
    func()
    stop=time.time()
    print('Run Time Is: %s' %(stop - start))

wrapper(index)

# 完美了吗?并没有,因为每次调用我们还需要传参...
import time

def index():
    print('welcome to index page')
    time.sleep(3)

def outter(func):
    def wrapper():
        start=time.time()
        func()
        stop=time.time()
        print('Run Time Is: %s' %(stop - start))
    return  wrapper

f=outter(index)
f()

# 变成"装饰器"写法,但是好像不满足两大原则,我们修改了函数的调用方式,下面的代码 ,搞定
import time

def index():
    print('welcome to index page')
    time.sleep(3)

def outter(func):
    def wrapper():
        start=time.time()
        func()
        stop=time.time()
        print('Run Time Is: %s' %(stop - start))
    return  wrapper

index=outter(index)
index()

装饰器升级版

代码语言:javascript
复制
import time
def home(name):
    print('welcome %s to home page' %name)
    time.sleep(2)

# 上面这个函数,是一个需要传参的函数,调用方法如下
home('zls')

# 需求,使用装饰器,调用
import time

def index():
    print('welcome to index page')
    time.sleep(3)

def home(name):
    print('welcome %s to home page' %name)
    time.sleep(2)

def outter(func):
    def wrapper(name):
        start=time.time()
        func(name)
        stop=time.time()
        print('Run Time Is: %s' %(stop - start))
    return  wrapper

home=outter(home)
home('zls')

# 完美?那么我们把index在调用一下试试?
import time

def index():
    print('welcome to index page')
    time.sleep(3)

def home(name):
    print('welcome %s to home page' %name)
    time.sleep(2)

def outter(func):
    def wrapper(name):
        start=time.time()
        func(name)
        stop=time.time()
        print('Run Time Is: %s' %(stop - start))
    return  wrapper

index=outter(index)
index()

# home=outter(home)
# # home('zls')

emm... 报错了,为啥呢?因为把为了写home函数的装饰器,把wrapper改了啊,需要一个参数

代码语言:javascript
复制
import time

def index():
    print('welcome to index page')
    time.sleep(3)

def home(name):
    print('welcome %s to home page' %name)
    time.sleep(2)

def outter(func):
    def wrapper(*args,**kwargs):
        start=time.time()
        func(*args,**kwargs)
        stop=time.time()
        print('Run Time Is: %s' %(stop - start))
    return  wrapper

index=outter(index)
index()

home=outter(home)
home('zls')

那么问题来了,如果函数有返回值怎么办?

代码语言:javascript
复制
import time

def index():
    print('welcome to index page')
    time.sleep(3)

def home(name):
    print('welcome %s to home page' %name)
    time.sleep(2)
    return 123

def outter(func):
    def wrapper(*args,**kwargs):
        start=time.time()
        func(*args,**kwargs)
        stop=time.time()
        print('Run Time Is: %s' %(stop - start))
    return  wrapper

home=outter(home)
res=home('zls')
print(res)

改进一下纸

代码语言:javascript
复制
import time

def index():
    print('welcome to index page')
    time.sleep(3)

def home(name):
    print('welcome %s to home page' %name)
    time.sleep(2)
    return 123

def timmer(func):
    def wrapper(*args,**kwargs):
        start=time.time()
        res=func(*args,**kwargs)
        stop=time.time()
        print('Run Time Is: %s' %(stop - start))
        return res
    return  wrapper

index=timmer(index)
index()

home=timmer(home)

res=home('zls')
print(res)

装饰器语法糖

代码语言:javascript
复制
import time

def timmer(func):
    def wrapper(*args,**kwargs):
        start=time.time()
        res=func(*args,**kwargs)
        stop=time.time()
        print('Run Time Is: %s' %(stop - start))
        return res
    return  wrapper

@timmer
def index():
    print('welcome to index page')
    time.sleep(3)

# 语法糖,把装饰器,放到被装饰对象上面加上@
@timmer
def home(name):
    print('welcome %s to home page' %name)
    time.sleep(2)
    return 123

index()

res=home('zls')
print(res)

装饰器模板

代码语言:javascript
复制
def outter(func):
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        return res
    return wrapper

@outter

小练习:认证功能的装饰器

代码语言:javascript
复制
import time

def auth(func):
    def wrapper(*args,**kwargs):
        inp_user=input('please input your username: ').strip()
        inp_pwd=input('please input your password: ').strip()

        if inp_user == 'zls' and inp_pwd == '123':
            print('login successfull')
            res=func(*args,**kwargs)
        return res
    return wrapper

@auth
def index():
    print('welcome to index page')
    time.sleep(3)

index()

叠加多个装饰器

代码语言:javascript
复制
import time

def timmer(func):
    def wrapper(*args,**kwargs):
        start=time.time()
        res=func(*args,**kwargs)
        stop=time.time()
        print('Run Time Is: %s' %(stop - start))
        return res
    return  wrapper

def auth(func):
    def wrapper(*args,**kwargs):
        inp_user=input('please input your username: ').strip()
        inp_pwd=input('please input your password: ').strip()

        if inp_user == 'zls' and inp_pwd == '123':
            print('login successfull')
            res=func(*args,**kwargs)
        return res
    return wrapper

@auth
@timmer
def index():
    print('welcome to index page')
    time.sleep(3)

index()

注意:当我们有多个装饰器的时候,解释语法是自下而上运行,但是如果是装饰器的话,自上而下运行,如果我们把timmer装饰器放在auth上面,那么运算的时间,会把auth函数的时间加进去

有参装饰器

之前我们用的都是无参装饰器,现在我们要让装饰器可以传递参数

我们来修改一下认证的源,之前我们认证都是文件,企业中会把用户存储在MySQL中或者LDAP

代码语言:javascript
复制
import time

def timmer(func):
    def wrapper(*args,**kwargs):
        start=time.time()
        res=func(*args,**kwargs)
        stop=time.time()
        print('Run Time Is: %s' %(stop - start))
        return res
    return  wrapper

def auth(func):
    def wrapper(*args,**kwargs):
        if engine == 'file':
            inp_user=input('please input your username: ').strip()
            inp_pwd=input('please input your password: ').strip()
            if inp_user == 'zls' and inp_pwd == '123':
                print('login successfull')
                res=func(*args,**kwargs)
                return res
            else:
                print('username or password error')
        elif engine == 'mysql':
            print('基于MySQL的认证机制')
        elif engine == 'ldap':
            print('基于LDAP的认证机制')
        else:
            print('无法识别的认证源')
    return wrapper

@auth
@timmer
def index():
    print('welcome to index page')
    time.sleep(3)
index()

代码写完了,但是我需要知道engine是什么,然后找对应的认证方式,需要传参使用闭包

代码语言:javascript
复制
import time

def timmer(func):
    def wrapper(*args,**kwargs):
        start=time.time()
        res=func(*args,**kwargs)
        stop=time.time()
        print('Run Time Is: %s' %(stop - start))
        return res
    return  wrapper

def engine_auth(engine='file'):
    def auth(func):
        def wrapper(*args,**kwargs):
            if engine == 'file':
                inp_user=input('please input your username: ').strip()
                inp_pwd=input('please input your password: ').strip()
                if inp_user == 'zls' and inp_pwd == '123':
                    print('login successfull')
                    res=func(*args,**kwargs)
                    return res
                else:
                    print('username or password error')
            elif engine == 'mysql':
                print('基于MySQL的认证机制')
            elif engine == 'ldap':
                print('基于LDAP的认证机制')
            else:
                print('无法识别的认证源')
        return wrapper
    return auth

@engine_auth('mysql')
@timmer
def index():
    print('welcome to index page')
    time.sleep(3)
index()

装饰器,最多就三层,最外面层可以随便传递参数


无参装饰器模板

代码语言:javascript
复制
def outter(func):
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        return res
    return wrapper

有参装饰器模板

代码语言:javascript
复制
def outter(x,y,z):
    def outter2(func):
        def wrapper(*args,**kwargs):
            res=func(*args,**kwargs)
            return res
        return wrapper
    return outter2

解决程序bug

代码语言:javascript
复制
import time

def timmer(func):
    def wrapper(*args,**kwargs):
        start=time.time()
        res=func(*args,**kwargs)
        stop=time.time()
        print('Run Time Is: %s' %(stop - start))
        return res
    return  wrapper

def engine_auth(engine='file'):
    def auth(func):
        def wrapper(*args,**kwargs):
            if engine == 'file':
                inp_user=input('please input your username: ').strip()
                inp_pwd=input('please input your password: ').strip()
                if inp_user == 'zls' and inp_pwd == '123':
                    print('login successfull')
                    res=func(*args,**kwargs)
                    return res
                else:
                    print('username or password error')
            elif engine == 'mysql':
                print('基于MySQL的认证机制')
            elif engine == 'ldap':
                print('基于LDAP的认证机制')
            else:
                print('无法识别的认证源')
        return wrapper
    return auth

@engine_auth('file')
@timmer
def index():
    print('welcome to index page')
    time.sleep(3)
index()


@engine_auth('file')
@timmer
def home(name):
    print('welcome %s to home page' %name)
    time.sleep(2)
    return 123
res=home('zls')
print(res)

我们在两个函数前面都加上认证模块

然后这个程序就智障了,mmp,让我登录两次?你们见过哪个网站登录过一次,还需要登录第二次?

比如你再京东买东西...一开始登录了,然后当你点购物车还要登录?点结算还要登录?疯了吧

所以我们需要记录一下用户的登录状态...

代码语言:javascript
复制
import time

current_user={'username':None}

def timmer(func):
    def wrapper(*args,**kwargs):
        start=time.time()
        res=func(*args,**kwargs)
        stop=time.time()
        print('Run Time Is: %s' %(stop - start))
        return res
    return  wrapper

def engine_auth(engine='file'):
    def auth(func):
        def wrapper(*args,**kwargs):
            if current_user['username']:
                print('已经登录过了,无需再次登录')
                res=func(*args,**kwargs)
                return res
            if engine == 'file':
                inp_user=input('please input your username: ').strip()
                inp_pwd=input('please input your password: ').strip()
                if inp_user == 'zls' and inp_pwd == '123':
                    print('login successfull')
                    current_user['username']=inp_user  #记录登录状态
                    res=func(*args,**kwargs)
                    return res
                else:
                    print('username or password error')
            elif engine == 'mysql':
                print('基于MySQL的认证机制')
            elif engine == 'ldap':
                print('基于LDAP的认证机制')
            else:
                print('无法识别的认证源')
        return wrapper
    return auth

@engine_auth('file')
@timmer
def index():
    print('welcome to index page')
    time.sleep(3)
index()


@engine_auth('file')
@timmer
def home(name):
    print('welcome %s to home page' %name)
    time.sleep(2)
    return 123
res=home('zls')
print(res)

装饰器伪装注释信息


了解函数注释的作用

代码语言:javascript
复制
import time

def demo(func):
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        return res
    return  wrapper

def index():
    """
    index功能
    """
    print('进入index页面')
    time.sleep(3)

# 查看函数名
print(index.__name__)

# 查看函数注释信息
print(index.__doc__)

# 查看函数的注释信息
print(help(index))

现在我们把装饰器加上

代码语言:javascript
复制
import time

def demo(func):
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        return res
    return  wrapper

@demo
def index():
    """
    index功能
    """
    print('进入index页面')
    time.sleep(3)

print('--- 函数名:---')
print(index.__name__)

print('--- 注释信息1 ---')
print(index.__doc__)

print('--- 注释信息2 ---')
print(help(index))

当我们给一个函数加上装饰器后,会发现,函数名变了,注释信息没了,这样的话,别人无法查看你的这个功能,用不明白,那就是垃圾代码。

那肿么办?我们把注释信息加在wrapper里面?

代码语言:javascript
复制
import time

def demo(func):
    def wrapper(*args,**kwargs):
        """
        index功能
        """
        res=func(*args,**kwargs)
        return res
    return  wrapper

@demo
def index():
    """
    index功能
    """
    print('进入index页面')
    time.sleep(3)

print('--- 函数名:---')
print(index.__name__)

print('--- 注释信息1 ---')
print(index.__doc__)

print('--- 注释信息2 ---')
print(help(index))

这样写,那就是智障...写死了,如果我把装饰器加在别的函数上呢?还是index功能嘛?

代码语言:javascript
复制
import time

def demo(func):
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        return res
    # 将wrapper函数的函数名赋值给func函数,也就是传递进来的函数(被装饰对象)
    wrapper.__name__ = func.__name__
    # 将wrapper函数的注释信息赋值给func函数,也就是传递进来的函数(被装饰对象)
    wrapper.__doc__ = func.__doc__
    return  wrapper

@demo
def index():
    """
    index功能
    """
    print('进入index页面')
    time.sleep(3)

print('--- 函数名:---')
print(index.__name__)

print('--- 注释信息1 ---')
print(index.__doc__)

print('--- 注释信息2 ---')
print(help(index))

但是...咱们可以看看一个函数下面有多少个__xxx__的方法,上面才有两个

我们需要使用python内置的一个装饰器

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

def demo(func):
    @wraps(func)
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        return res
    return  wrapper

@demo
def index():
    """
    index功能
    """
    print('进入index页面')
    time.sleep(3)

print('--- 函数名:---')
print(index.__name__)

print('--- 注释信息1 ---')
print(index.__doc__)

print('--- 注释信息2 ---')
print(help(index))

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-04-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 装饰器介绍
  • 小练习:认证功能的装饰器
  • 叠加多个装饰器
  • 有参装饰器
  • 装饰器伪装注释信息
相关产品与服务
云数据库 MySQL
腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档