zoukankan      html  css  js  c++  java
  • 7.11函数(三)

    一、闭包函数

    1.什么是闭包函数:

    (1)闭:定义在函数内部的函数

    (2)包:内部函数引用了外部函数作用域的名字

    2.给函数传值的两种方式

    (1)传参

    def index(name):
        print(name)
    index('francis')  # 传参

    (2)闭包

    def outter(x,y):
        # x = 1 通过传参获得
        # y = 40 通过传参获得
        def my_max():
            if x > y:
                return x
            return y
        return my_max
    res = outter(1,40)  # res就是my_max函数的内存地址
    print(res())  # 调用函数my_max,会去函数outter中取参数,这就是内部函数引用了外部函数作用域的名字

    二、装饰器

    1.什么是装饰器:

    装饰:给被装饰对象添加新的功能

    器:就是一个工具

    2.为什么要用装饰器

    (1)装饰器遵循开放封闭的原则

    开放:对扩展开放(能对原来的函数添加功能)

    封闭:对修改封闭(不能修改原来的函数功能)

    (2)装饰器(是可调用对象)必须遵循的两个原则

      1.不改变被装饰对象(原函数)的源代码

      2.不改变被装饰对象(原函数)的调用方式

    3.补充知识:

    import time  # 调用模块
    print(time.time())  # 打印时间戳,打印的是当前时间距离1970-1-1 00:00:00相差的秒数(1970-1-1 00:00:00是Unix诞生元年)
    time.sleep(3)  # 让cpu睡三秒,传参数,表示让你的程序暂停几秒
    print('程序停了3秒')

    三、装饰器简单版本

    1.用函数实现让程序停3秒,再执行打印功能

    import time
    def index():
        time.sleep(3)
        print('程序停了3秒')
    index()

    2.如何实现:

    统计执行index函数(上面的)共用了几秒,且不改变原函数的调用方式和源代码。

    import time
    def index():
        time.sleep(3)
        print('程序停了3秒')
    
    def outter(func):  # func = index  就是最原始的index函数的内存地址
        def get_time():
            start = time.time()  # 统计时间戳
            func()  # func = index  加括号调用最原始的index函数(让程序停3秒的函数)
            end = time.time()  # 统计时间戳
            print('index运行了:%s'%(end-start))  # 两次统计时间戳,用了几秒
        return get_time  # 返回get_time函数的内存地址
    index = outter(index)  # outter(index)执行之后返回的是get_time函数的内存地址
    # 所以第一个index指向get_time函数的内存地址,第二个index是最原始的index函数内存地址(让程序停3秒的函数)
    index()  # 就相当于get_time函数加上括号调用了

    四、装饰器升级版

    1.如何实现:

    用一个装饰器装饰两个函数,其中一个不用传参,一个需要传参,且该装饰器的返回值就是原函数的返回值,原函数:

    import time
    def index():
        time.sleep(3)
        print('程序停了3秒')
        return 'index的返回值'
    res = index()
    print(res)
    
    def login(name):
        time.sleep(1)
        print('%s是帅比'%name)
        return 'login的返回值'
    res1 = login('francis')
    print(res1)

    2.实现:

    import time
    def index():
        time.sleep(3)
        print('程序停了3秒')
        return 'index的返回值'
    
    def login(name):
        time.sleep(1)
        print('%s是帅比'%name)
        return 'login的返回值'
    
    def outter(func):  # func = 最原始的index或者login函数的内存地址
        def get_time(*args, **kwargs):  # *和**在形参中的用法,接收多余的位置参数和关键字参数
            start = time.time()
            res = func(*args, **kwargs)  # 最原始的index或者login函数的内存地址,加括号调用,并接收原函数的返回值
            # *和**在实参中的用法,打散容器类型中的参数和字典中的参数
            end = time.time()
            print('%s函数运行了:%s'%(func,end-start))
            return res  # 返回原函数的返回值
        return get_time
    index = outter(index)  # 第二个index是最原始的index函数的内存地址
    res = index()
    print(res)  # 原index函数的返回值
    
    login = outter(login)  # 第二个login是最原始的login函数的内存地址
    res1 = login('francis')
    print(res1)  # 原login函数的返回值
    View Code

    实现了无参函数和有参函数都可以直接调用同一个装饰器(装饰器可以接收任意数量的参数)

    五、装饰器语法糖

    1.上述实现的装饰器中,如果想要装饰某个函数,需要代码比如:index = outter(index)、login = outter(login)来实现,很麻烦,这时候可以用到装饰器语法糖。

    import time
    
    def outter(func):  # func = 最原始的index或者login函数的内存地址
        def get_time(*args, **kwargs):  # *和**在形参中的用法,接收多余的位置参数和关键字参数
            start = time.time()
            res = func(*args, **kwargs)  # 最原始的index或者login函数的内存地址,加括号调用,并接收原函数的返回值
            # *和**在实参中的用法,打散容器类型中的参数和字典中的参数
            end = time.time()
            print('%s函数运行了:%s'%(func,end-start))
            return res  # 返回原函数的返回值
        return get_time
    
    @outter  # index = outter(index)
    def index():
        time.sleep(3)
        print('程序停了3秒')
        return 'index的返回值'
    
    @outter  # login = outter(login)
    def login(name):
        time.sleep(1)
        print('%s是帅比'%name)
        return 'login的返回值'
    
    res = index()
    print(res)  # 原index函数的返回值
    res1 = login('francis')
    print(res1)  # 原login函数的返回值
    View Code

    @outter会将紧挨着他的可调用对象当作他的参数传入直接执行,并接收返回值

    2.书写规范:

    语法糖在书写的时候应该与被装饰对象紧紧挨着,两者之间不要有空格

    六、装饰器模板

    def outter(func):
        def inner(*args,**kwargs):
            print('执行被装饰函数之前,你可以做的操作')
            res = func(*args,**kwargs)
            print('执行被装饰函数之后,你可以做的操作')
            return res
        return inner

    1.如何实现一个认证装饰器:

    执行函数index之前必须先输入用户名和密码,正确之后才能执行index,如果之后再执行函数login,则不需要再登录

    import time
    user_dic = {'is_login':None}  # 全局定义一个空字典,判断用户是否已登录过
    def outter(func):
        def inner(*args,**kwargs):
            if user_dic['is_login']:  # 判断是否已登录
                res = func(*args, **kwargs)  # 登陆过直接执行原函数
                return res
            else:
                username = input('please input your username>>>:').strip()
                password = input('please input your password>>>:').strip()
                if username == 'francis' and password == '123':
                    user_dic['is_login'] = True  # 登陆成功后,修改字典为True
                    res = func(*args,**kwargs)
                    return res
                else:
                    print('username or password error')
        return inner
    
    @outter  # index=outter(index)
    def index(name):
        time.sleep(1)
        print('%s是帅比'%name)
        return 666
    
    @outter  # login=outter(login)
    def login():
        time.sleep(1)
        print('from login')
        return 999
    
    print(index('francis'))
    print(login())
    View Code

    七、多层装饰器

    1.给index函数套多个装饰器,使index函数先执行认证功能再统计函数运行时间

    import time
    user_dic = {'is_login':None}  # 全局定义一个空字典,判断用户是否已登录过
    def outter(func):
        def inner(*args,**kwargs):
            if user_dic['is_login']:  # 判断是否已登录
                res = func(*args, **kwargs)  # 登陆过直接执行原函数
                return res
            else:
                username = input('please input your username>>>:').strip()
                password = input('please input your password>>>:').strip()
                if username == 'francis' and password == '123':
                    user_dic['is_login'] = True  # 登陆成功后,修改字典为True
                    res = func(*args,**kwargs)
                    return res
                else:
                    print('username or password error')
        return inner
    
    def outter1(func):  # func = 最原始的index或者login函数的内存地址
        def get_time(*args, **kwargs):  # *和**在形参中的用法,接收多余的位置参数和关键字参数
            start = time.time()
            res = func(*args, **kwargs)  # 最原始的index或者login函数的内存地址,加括号调用,并接收原函数的返回值
            # *和**在实参中的用法,打散容器类型中的参数和字典中的参数
            end = time.time()
            print('%s函数运行了:%s'%(func,end-start))
            return res  # 返回原函数的返回值
        return get_time
    
    @outter  # index=outter(get_time)   index=inner函数的地址
    @outter1  # get_time=outter1(index)   index是最原始的index的内存地址
    def index():
        time.sleep(3)
        print('index')
        return 'from index'
    index()
    View Code

    方法是在index函数上套两个装饰器语法糖

    2.着重注意:

    (1)装饰器在装饰函数的时候,执行顺序是是从下往上(紧贴函数到远离函数)

    (2)装饰器在执行的时候,执行顺序是从上往下的(离被装饰的函数最远的装饰器最先执行,最后执行的是紧贴函数的装饰器)

    八、装饰器修复技术

    1.作用

    (1)用户查看被 装饰了的函数 的函数名的时候,看到的就是 原函数 的内存地址

    (2)用户查看被 装饰了的函数 的注释的时候,看到的就是 原函数 的注释

    2.如何用

    from functools import wraps
    def outter(func):
        @wraps(func)  # 装饰器修复技术
        def inner(*args,**kwargs):
            '''
            这是inner函数
            :param args:
            :param kwargs:
            :return:
            '''
            print('执行被装饰函数之前 你可以执行的操作')
            res = func(*args,**kwargs)
            print('执行被装饰函数之后 你可以执行的操作')
            return res
        return inner
    
    @outter  # index = outter(最原始的index的内存地址)
    def index():
        '''
        这是index函数
        :return:
        '''
        pass
    print(index)  # 查看函数的内存地址  返回的就是原函数index的
    print(help(index))  # 查看函数的注释  返回的就是原函数index的
    print(index.__name__)  # 查看函数名字符串形式  返回的就是index
    View Code

    (1)在装饰器外导入一个模块:from functools import wraps

    (2)在装饰器里写入:@wraps(func),括号内的形参与装饰器内的一致

    九、多层装饰器

    1.习题:求index运行之后的打印结果

    def outter1(func1):
        print('加载了outter1')
        def wrapper1(*args,**kwargs):
            print('执行了wrapper1')
            res1=func1(*args,**kwargs)
            return res1
        return wrapper1
    def outter2(func2):
        print('加载了outter2')
        def wrapper2(*args,**kwargs):
            print('执行了wrapper2')
            res2=func2(*args,**kwargs)
            return res2
        return wrapper2
    def outter3(func3):
        print('加载了outter3')
        def wrapper3(*args,**kwargs):
            print('执行了wrapper3')
            res3=func3(*args,**kwargs)
            return res3
        return wrapper3
    @outter1  # index = outter1(wapper2)
    @outter2  # wrapper2 = outter2(wrapper3)
    @outter3  # wrapper3 = outter3(index)  最原始的index的内存地址
    def index():
        print('from index')
    index()
    View Code

    2.代码执行顺序:

  • 相关阅读:
    每日作业报告之《大道至简》读后感
    Java学习的第二十六天
    Java学习的第二十五天
    封装按钮组件
    回车导致地址栏出现?
    判断用户输入值的是不是整数
    String类型在JVM中的内存分配
    MySQL索引的原理及使用
    ICPC Yokohama, 2018(Domestic)C. Skyscraper “MinatoHarukas ” (暴力)
    CH6803 导弹发射塔(二分图最大匹配/多重匹配)
  • 原文地址:https://www.cnblogs.com/francis1/p/11178574.html
Copyright © 2011-2022 走看看