zoukankan      html  css  js  c++  java
  • 12.装饰器,迭代器,生成器

    • 引子
    • 装饰器

    • 装饰器语法糖

    • 无参装饰模板

    • 叠加多个装饰器

    • 迭代器

    • 生成器


    • 装饰器

    • 1、什么是装饰器

      装饰器就是一个用来为被装饰对象添加新功能的工具
      装饰器指的定义一个函数,该函数是用来为其他函数添加额外的功能
    • 2、为何要用装饰器

      在添加新功能的时候要遵循开放封闭的原则:一旦软件上线运行之后,应该对修改源代码封闭,对扩展功能开放
      原则:
      1、不修改函数内的源代码
      2、不修改函数的调用方式
      装饰器就是在遵循原则1和2的前提下,为被装饰对象添加上新功能
    • 3、如何实现装饰器

      函数装饰器分为:无参装饰器和有参装饰两种,二者的实现原理一样,
      都是“函数嵌套+闭包+函数对象”的组合使用的产物
    # 需求:在不修改index函数的源代码和调用方式的前提下为其添加统计运行时间的新功能
    import time    # 导入时间模块
    
    def index():   # 一个函数
        time.sleep(1)   # 模拟index运行的一点时间   源代码
        print('from index')                    # 源代码
    
    index()     # 调用方式
    
    
    
    # 方案一:失败
    # 问题:确实添加了新功能,但是修改了源代码
    import time
    
    def index():
        start = time.time()   # 统计开始时间
        time.sleep(1)
        print('from index')
        stop = time.time()    # 统计结束时间
        print('run time is %s' %(stop - start))
    
    index()
    
    
    # 方案二:失败
    # 问题:看似装饰器的效果实现了,但是引发了重复代码的问题
    import time
    
    def index():
        time.sleep(1)
        print('from index')
    
    start = time.time()
    index()
    stop = time.time()
    print('run time is %s' %(stop - start))
    
    
    # 方案三:失败
    # 问题:把要装饰的代码丢进函数里,为的是重复调用,但是把index写死了,就无法用上装饰器
    import time
    
    def index():
        time.sleep(1)
        print('from index')
    
    def wrapper():
        start = time.time()
        index()
        stop = time.time()
        print('run time is %s' %(stop - start))
    
    wrapper()
    wrapper()
    
    
    # 方案四:失败
    # 问题: 把index函数名变成变量名func,要为wrapper函数的函数体代码传参数,
    #       直接用参数传参,装饰对象虽然写活了,但只能装饰index
    import time
    
    def index():
        time.sleep(1)
        print('from index')
    
    def wrapper(func):
        start = time.time()
        func()              # 被装饰函数
        stop = time.time()
        print('run time is %s' %(stop - start))
    
    wrapper(index)
    
    
    # 方案五:====》闭包函数
    import time
    
    def index():       # index = 被装饰对象index函数的内存地址
        time.sleep(1)
        print('from 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    # 千万别加括号
                           # outter=(被装饰对象index函数的内存地址)
    index = outter(index)  # outter-》返回wrapper函数的内存地址-》index = wrapper函数的内存地址
    print(index)
    index()
    
    # 方案六:====》将wrapper伪装成被装饰的函数,name应该装的尽可能像一些
    import time
    
    def index():   # index = 被装饰对象index函数的内存地址
        time.sleep(1)
        print('from index')
        
    def home(name):
        time.sleep(2)
        print("welcome %s to home page" %name)
        return 123
        
    def outter(func):     # func = 被装饰对象index函数的内存地址
    	def wrapper(*args,**kwargs):
    		start = time.time()
    		res = func(*args,**kwargs)  # 被装饰对象index函数的内存地址(x,y,z)
    		stop = time.time()
    		print('run time is %s' %(stop - strt))
       		return res
    	return wrapper
    
    index = outter(index) 
    home = outter(home)  # 偷梁换柱:home这个名字指向的wrapper函数的内存地址
    
    res = home("geng")  # wrapper("geng")
    print(res)
    
    res = index()   # wrapper()
    print(res)
              
    
    • 装饰器语法糖

      语法糖:让你开心的语法
    import time
    from functools import wraps
    
    def outter(func):  # func = 被装饰对象index函数的内存地址
        @wraps(func)
        def wrapper(*args,**kwargs):
            start = time.time()
            res = func(*args,**kwargs)  # 被装饰对象index函数的内存地址(x,y,z)
            stop = time.time()
            print('run time is %s' %(stop - start))
            return res
        return wrapper  # 千万别加括号
    
    # 在被装饰对象正上方的单独一行写@装饰器名字
    @outter  # index = outter(index)  # outter(被装饰对象index函数的内存地址) -> 返回wrapper
                                      # 函数的内存地址---> index = wrapper函数的内存地址
    def index():  # index = 被装饰对象index函数的内存地址
        """这是index函数"""
        time.sleep(1)
        print('from index')
    
    @outter  # home = outter(home)
    def home(name):
        time.sleep(2)
        print("welcome %s to home page" %name)
        return 123
    
    # res = home("egon")  # wrapper("egon")
    # print(res)
    
    # res = index()  # wrapper()
    # print(res)
    
    print(index.__name__)
    print(index.__doc__)
    
    • 无参装饰器模板

    # 闭包函数 函数 (被装饰函数)
    def outter(func):     
        def wrapper(*args,**kwargs):   # wrapper函数,在wrapper内写被装饰的函数
            res = func(*args,**kwargs)  # *args,**keargs让参数保持一致,装的像一点
            return res       # 让返回值保持一致,装的像一点
        return wrapper     
    # 以上的装饰器为被装饰对象啥新功能也没添加,就是无参装饰器模板
    
    # 认证功能装饰器模板
    def login(func):
        def wrapper(*args,**kwargs):
            name = input('username>>>: ').strip()
            pwd = input('password>>>: ').strip()
            if name == 'egon' and pwd == "123":
                res = func(*args,**kwargs)
                return res
            else:
                print('账号密码错误')
        return wrapper
    
    @login
    def index():
        print('index')
    
    index()
    
    
    • 叠加多个装饰器

    def deco1(func1):     # func1 = wrapper2的内存地址
        def wrapper1(*args,**kwargs):
            print('==========>wrapper1')
            res1 = func1(*args,**kwargs)
            print('end1')
            return res1
        return wrapper1
    
    def deco2(func2):    # func2 = wrapper3的内存地址
        def wrapper2(*args,**kwargs):
            print('=========>wrapper2')
            res2 = func2(*args,**kwargs)
            print('end2')
            return res2
        return wrapper2
    
    def deco3(func3):    # func3 = 被装饰index的内存地址
        def wrapper3(*args,**kwargs):
            print('=========>wrapper3')
            res3 = func3(*args,**kwargs)
            print('end3')
            return res3
        return wrapper3
    #                           index = wrapper1的内存地址
    @deco1 # deco1(wrapper2的内存地址) -> wrapper1的内存地址
    @deco2 # deco2(wrapper3的内存地址) -> wrapper2的内存地址
    @deco3 # deco3(被装饰index的内存地址)-> wrapper3的内存地址
    def index():
    	print('index......')
    	
    index()           
    

    • 迭代器

    • 1、什么是迭代器

      迭代器指的是迭代取值的工具

      什么是迭代?

      迭代就是一个重复的过程,但是每一次重复都是在上一次的基础之上进行的,单纯的重复不是迭代
    nums = [111, 222, 333]  # 遍历列表,索引,迭代取值
    nums = "hello"          # 遍历字符串,索引,迭代取值
    
    def get(l):     # 函数就是取值工具
        i = 0
        while i < len(l):
        	print(l[i])
        	i += 1
    
    get(nums)   
    
    • 2、为何要用迭代器

      1、迭代器提供了一种不依赖于索引的、通用的迭代取值方案
      2、节省内存
    • 3、如何用迭代器

      可迭代的对象
      1、内置有_iter_方法的对象都叫可迭代的对象
      调用可迭代对象的_iter_方法,返回的是它的迭代器
      迭代器对象
      1、内置有_next_方法
      2、内置有_iter_方法
      调用迭代器对象的_iter_方法得到的它自己,就跟没调用一样
      调用迭代器对象的_next_方法返回下一个值,不依赖索引
      可以一直调用_next_直到取干净,则抛出异常StopIteration
    # 内置的类型都是可迭代的对象
    "abc"        # 字符串
    [1,2,3]      # 列表
    (1,2,3)      # 元组
    {'k1':111}   # 字典
    {1,2,3}      # 集合
    f = open('a.txt',mode='wt')  # 文件对象本身就是迭代器对象
    
    # 例1:
    # 迭代器直接取值方式
    nums = [111,222,3333]
    nums_iter = nums.__iter__()     # 将可迭代对象转为迭代器版本,源列表没动,产生新的
    print(nums_iter)
    print(nums_iter.__next__())
    print(nums_iter.__next__())
    print(nums_iter.__next__())
    print(nums_iter.__next__())   # 抛出异常 StopIteration
    
    
    x = iter(nums)
    print(next(x))
    print(next(x))
    print(next(x))
    
    # 例2:
    # 用while循环解决迭代器取值
    nums = [111,222,3333]
    nums_iter = iter(nums)  # 把可迭代对象先转成迭代器对象
    while True:
        try:   # 异常处理,next到第四次检测出异常
            res = next(nums_iter)
            print(res)
        except StopIteration:  # 捕捉异常,结束循环
            break
    
    # 1、先调用in后那个对象的__iter__方法,拿到迭代器对象
    # 2、 res = next(迭代器对象),执行一次循环体代码
    # 3、循环往复步骤2,直到值取干净抛出异常StopIteration,for会捕捉异常结束循环
    
    nums = [111,222,3333]   # 用for循环两行代码解决例2
    for res in nums:
        print(res)
    
    • 迭代器的优缺点

      优点:

      ​ 1、为序列和非序列类型提供了一种统一的迭代取值方式。

      ​ 2、惰性计算:迭代器对象表示的是一个数据流,可以只在需要时才去调用next来计算出一个值,就迭代器本身来说,同一时刻在 内存中只有一个值,因而可以存放无限大的数据流,而对于其他容器类型,如列表,需要把所有的元素都存放于内存中,受内 存大小的限制,可以存放的值的个数是有限的。缺点

      缺点:

      ​ 1、除非取尽,否则无法获取迭代器的长度

      ​ 2、只能取下一个值,不能回到开始,更像是‘一次性的’,迭代器产生后的唯一目标就是重复执行next方法直到值取尽,否则就会 停留在某个位置,等待下一次调用next;若是要再次迭代同个对象,你只能重新调用iter方法去创建一个新的迭代器对象,如 果有两个或者多个循环使用同一个迭代器,必然只会有一个循环能取到值。


    • 生成器

    • yield 与 return的异同

      相同点:返回值层面用法一样
      不同点:return只能返回值一次,而yield可以返回多次
    def func():
        print('xxx')
        yield 111
        print('yyy')
        yield 222
        print('zzz')
        yield 333
        print('mmmm')  # 报错,抛出异常 StopIteration 取到这里就取干净了
    
    # 当函数内出现yield关键字,再调用函数并不会触发函数体代码的运行,会返回一个生成器
    g = func()
    # print(g)  # 生成器就是一种自定义的迭代器
    
    
    res = next(g)
    print(res)
    
    res = next(g)
    print(res)
    
    res = next(g)
    print(res)
    
    next(g)
    
    # 例:
    # 一出场就是一只老母鸡,可以存无穷个值的类型,但每次只能拿一个
    def func():
        res = 1
        while True:
            yield res
            res += 1
    
    g = func()  # 得到一个迭代器
    
    print(next(g))   # 打印next触发代码运行,可以返回无数个值
    print(next(g))
    print(next(g))
    
    
    
    nums =[111,22,33]
    
    x = iter(nums)
    for res in x:  # x.__iter__()
        print(res)
    print('='*50)
    
    for res in x:  # x.__iter__()
        print(res)
    
    
    for res in nums:  # nums.__iter__()
        print(res)
    print('='*50)
    for res in nums:  # nums.__iter__()
        print(res)
    
  • 相关阅读:
    chrome浏览器,阻止回调方法中的window.open()方法
    安装1.4.2版本的Django
    noindex与nofllow标签的作用和用法
    mybatis中写sql语句时需要转义的字符
    GridView 导出到Excel
    (转)利用Cache防止同一帐号重复登录 (c#) .
    利用NPOI 导出文件到Excel (导出合同)
    TreeView控件的属性和事件【转】
    C# 数字货币转换大写方法
    Server 2003 IIS 发布网站
  • 原文地址:https://www.cnblogs.com/gfeng/p/14213135.html
Copyright © 2011-2022 走看看