zoukankan      html  css  js  c++  java
  • python学习的第十二天函数part3 装饰器

    1、什么是装饰器

    器=》工具

    装饰=》指的是为被装饰对象添加新功能

    装饰器本身可以是任意可调用的对象=>函数   被装饰的对象也可以是任意可调用的对象=》函数

    目标:写一个函数来为另一个函数添加新功能

    2、为何要用装饰器

    开放封闭原则:软件一旦上线就应该对修改封闭,对扩展开放

    即:对修改封闭:不能修改功能的源代码,也不能修改功能的调用方式

    对扩展开放:可以为原有的功能添加新的功能

    装饰器就是要在不修改功能源代码以及调用方式的前提下为原功能添加额外新的功能

    3.如何用装饰器(思路步骤)******

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

    我们要把index函数添加一个计时功能,但不能改变源代码和调用方式

    如果直接添加的话会改变源代码,所以我们要重定义一个有该功能的函数

     def wrapper():
            start=time.time()
            func() # 最原始那个index的内存地址()
            stop=time.time()
            print('run time is %s' %(stop-start))

    接下来如何使用?有2种办法

    1、直接加参数index              2、在外面重新定义一个函数成为闭包函数,间接取值

    第一种虽然简单,但若是重复使用该函数就会变得不方便,而且也改变的原函数的调用方式

    原来用index()执行,现在用index执行;第二种却有可以改善的余地,如下

    def outter(func):
        # func=最原始那个index的内存地址
        def wrapper():
            start=time.time()
            func() # 最原始那个index的内存地址()
            stop=time.time()
            print('run time is %s' %(stop-start))
        return wrapper
    
    index=outter(index) #index=outter(最原始那个index的内地址) #index=wrapper函数的内地址
    index() #wraper()     
    将Index强行等于outter(index)偷梁换柱,使Index的等于wrapper,和原来的index不一样,但是这样
    调用的方式却一样了,别人调用的时候还是index()

    为了让装饰器模拟的更像,我们还要再加一些功能,比如返回值可一样、原函数的参数也能传值

    我们将index的返回值设置为123
    def index(): print(
    'welcome to index page') time.sleep(3) return 123

    (1)模拟原函数的返回值

    当我们用装饰器修饰时候,实际用的是wrapper函数,也就是说我们需要把wrapper的返回值等于原来index的返回值。将res=func(),拿到返回值,然后在wrapper下return res ,就把返回值拿到手了

    def outter(func):
        # func=最原始那个index的内存地址
        def wrapper():
            start=time.time()
            res=func() # 最原始那个index的内存地址()
            stop=time.time()
            print('run time is %s' %(stop-start))
            return res
        return wrapper
    
    index=outter(index) #index=outter(最原始那个index的内地址) #index=wrapper函数的内地址
    #==============================================================
    
    res=index() #res=wraper()
    print(res)

    (2)模拟原函数的参数传值

    重新设置一个带参的原函数

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

    原函数已经被包装,home(name) 本质上是 wrapper(name),然后传给函数里面的home(name),

    上一节课学到参数几种类型,其中*args和**kwargs可以组合在一起可以传任何类型的参数

    def outter(func):
        # func=最原始那个home的内地址
        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

    4、装饰器的语法糖

    @装饰器的名字;要在被装饰对象正上方单独一行写上

    通常函数里有注释信息等一系列的说明,为了让装饰器拥有原函数的信息,需要导用另一个自带的模块     可以用print(help (home))   print(home._name_)查看原函数相关信息

    from functools import wraps
    
    import time
    def timmer(func): # func=最原始那个home的内地址
        @wraps(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

    5、有参装饰器

    首先无参装饰器模板:

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

    为某个函数添加认证功能,如果以前登录过了,无需输入账号密码直接执行功能

    user_info={'current_user':None}                可变量
    
    def auth(func):
        def wrapper(*args,**kwargs):
            if user_info['current_user'] is not None:
                res=func(*args,**kwargs)
                return res
            inp_user=input('username>>>: ').strip()
            inp_pwd=input('password>>>: ').strip()
            if inp_user == 'egon' and inp_pwd == '123':
                # 记录登录状态
                user_info['current_user']=inp_user           站在局部却改了全局的量
    
                print('login successful')
                res=func(*args,**kwargs)
                return res
            else:
                print('user or password error')
        return wrapper

    补充:gilobal与nonlocal

    比如

    # x=1
    # def func():
    #    x=2
    #
    # func()
    # print(x)              站在全局所以先用全局的量

    但如果变量是可变的话,x的量发生了改变,由此我们可以用可变量的方式在局部改变全局

    # x=[]
    # def func():
    #    x.append(1)
    #    x.append(2)
    #    x.append(3)
    #
    # func()
    # print(x)                   x=[1,2,3]

    gipbal:在局部声明变量是全局变量

    # x=1
    # def func():
    #     global x
    #     x=2
    #
    # func()
    # print(x)                         2

     nonlocal:在局部声明变量是外层函数的变量

    x=333
    def f1():
        x=222
        def f2():
            x=111
            def f3():
                nonlocal x
                x=0
            f3()
            print('f2内部的x: ',x)                  x=0
        f2()
        print('这是f1内部的x: ',x)                   x=222
    
    f1()
    print(x)                                         333

    有参装饰器模板:

    def outter2(xxx,yyy):
        def outter(func):
            def wrapper(*args,**kwargs):
                res=func(*args,**kwargs)
                print(xxx)
                print(yyy)
                return res
            return wrapper
        return outter
    

    写某个函数添加认证功能,并且如果以前登录过了,无需输入账号密码直接执行,还要知道该用户基于哪个文件或数据库登录的

    import time
    
    user_info={'current_user':None}
    
    def auth2(engine='file'):
        def auth(func):
            def wrapper(*args,**kwargs):
                if user_info['current_user'] is not None:
                    res=func(*args,**kwargs)
                    return res
                inp_user=input('username>>>: ').strip()
                inp_pwd=input('password>>>: ').strip()
    
                if engine == 'file':
                    print('基于文件的认证')
                    if inp_user == 'egon' and inp_pwd == '123':
                        # 记录登录状态
                        user_info['current_user']=inp_user
    
                        print('login successful')
                        res=func(*args,**kwargs)
                        return res
                    else:
                        print('user or password error')
                elif engine == 'mysql':
                    print('基于mysql数据的认证')
                elif engine == 'ldap':
                    print('基于ldap的认证')
                else:
                    print('无法识别认证源')
            return wrapper
        return auth
    -------------------------------------------------------------------------------------------------------
    @auth2(engine='mysql') # @auth ===> index=auth(最原始那个index的内存地址)===》index=wrapper
    def index():
        """这是index功能"""
        print('welcome to index page')
        time.sleep(2)
        return 123
    
    @auth2(engine='file')
    def home(name):
        """这是home功能"""
        print('welcome %s to home page' %name)
        time.sleep(1)
    
    index() #wrapper()
    home('egon')
  • 相关阅读:
    使用AudioRecord录音
    使用MediaRecorder录音
    程序员不应迷失方向
    鼠标移入下划线展开 CSS3伪类
    横向滚动条,鼠标按下左右滚动!
    js遍历数组的几种方法
    前端随心记---------nodejs工具nvm.nrm.nodemen
    前端随心记-----------面试题分享
    鹅厂前端面试小题
    前端随心记-------浅谈Axios
  • 原文地址:https://www.cnblogs.com/ye-hui/p/9715196.html
Copyright © 2011-2022 走看看