• Python基础(五)迭代器、生成器和装饰器


    迭代器

    迭代器协议

    迭代定义:

    迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果。每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值。

    1迭代器协议:对象必须提供一个__next__() 方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代(只能往前,不能退后)

    2可迭代对象(迭代器):实现了迭代器协议的对象(如何实现迭代器协议,在对象内部定义__iter__()方法

    3协议是一种约定,可迭代对象实现了迭代器协议,Python的内部函数工具(如for循环,sum,min,max函数等)都是使用迭代器协议访问对象!!!

    迭代器作用:可以节省内存

    迭代器相关的方法:iter() 和 next()

    python内置函数 next() 本质就是在调用__next__()

    >>> list=[1,2,3,4]
    >>> it = iter(list)    # 创建迭代器对象
    >>> print (next(it))   # 输出迭代器的下一个元素
    1
    >>> print (next(it))
    2
    >>>

    通过iter()还可以指定迭代至结束时的对象

    l = ['a', 'b', 'c', 'd']
    
    def test():
        return l.pop()
    
    x = iter(test, 'b')
    print(x.__next__()) #d
    print(x.__next__()) #c
    print(x.__next__()) #到'b'了,抛异常

    python中的for机制

    (字符串str,列表list,元组tunple,字典dict,,集合set,文件对象)这些都不是可迭代对象,只不过在for循环,调用了他们的内部的__iter__()方法,把他们变成了可迭代对象,(补充:为什么str,list,tunple可以有下标,例如list[0],因为他们是有序的)

    for循环把他们变成了可迭代对象后,就调用可迭代对象的_next()_方法去取值,而且for循环会捕获StopIteration异常,以终止迭代!!!

    1 name = 'ales'
    2 print(n)
    3 
    4 输出结果
    5 <str_iterator object at 0x00E7EF90> 

    for循环就是基于迭代器协议提供一个统一的可以遍历所有对象的方法,即在遍历之前,先调用对象的__iter__()方法将其转换成一个迭代器,然后使用迭代器协议去实现循环访问。这样所有的对象都可以通过for循环来遍历。name = 'ales'n = name.__iter__() #遵循迭代协议,调用_iter_方法生成可迭代对象

    print(n.__next__())
    print(n.__next__())
    print(n.__next__())
    print(n.__next__())
    print(n.__next__())# 迭代至StopIteration异常
    
    输出结果
    a
    l
    e
    s
    Traceback (most recent call last):
      File "E:/PycharmProjects/untitled/day18/迭代器.py", line 9, in <module>
        print(n.__next__())
    StopIteration
    

     

     利用while模仿for循环

    1 list1 = [1, 2, 3, 4]
    2 l = list1.__iter__()
    3 while True:
    4     try:
    5         print(l.__next__())
    6     except StopIteration:
    7         break
    执行结果

    1
    2
    3
    4 

    生成器

    什么是生成器

    可以理解为一种数据类型,这种数据类型自动实现了迭代器协议,其他的数据类型需要需要调用自己内置的__iter__方法,所以生成器就是可迭代对象

    python中有两种方式提供生成器:

    1.生成器函数:常规函数定义,但是使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中,挂起函数的状态,以便下次从它离开的地方继续执行

    2.生成器表达式:类似于列表解析,但是生成器返回按需产生结果的一个对象,而不是一次构建出的结果列表

    生成器函数

    def test():
        yield 1
        yield 2
        yield 3
    
    x = test()
    print(x)
    print(x.__next__())
    print(x.__next__())
    print(x.__next__())
    print(x.__next__())
    
    
    执行结果
    <generator object test at 0x00C834F0>
    1
    2
    3
        print(x.__next__())
    StopIteration 

    生成器函数和普通函数

    普通函数

    # 普通函数
    def test():
        print('start')
    
    test()
    
    运行结果
    start
    

    生成器函数

    # 生成器函数是一个可迭代对象,注意与普通函数的区别
    def test():
        print('start')
        yield '这是第一个yield语句的返回值'
        print('这是第一个yield的下一行')
        
        yield '这是第二个yield语句的返回值'
        print('这是第二个yield的下一行')
        
        yield '这是第三个yield语句的返回值'
        print('这是第三个yield的下一行')
    
    a = test() #生成一个可迭代对象 
    print(a) 
    print(type(a)) 
    
    运行结果 
    <generator object test at 0x02DF35B0> 
    <class 'generator'>

      接上

    第一种

    a.__next__() #运行至第一个yield语句结束,并保留运行状态
    a.__next__() #从上次的运行状态开始,运行至第二个yield语句结束
    a.__next__()
    
    运行结果
    start
    这是第一个yield的下一行
    这是第二个yield的下一行 

    第二种

    res = a.__next__() ##运行至第一个yield语句结束,保留运行状态,并将yield的返回值赋值给res
    print(res)
    运行结果
    start
    1 

    第三种

    res = a.__next__() #运行至第一个yield语句结束,保留运行状态,并将yield的返回值赋值给res
    print(res)
    a.__next__() #从上次的运行状态开始,运行至第二个yield语句结束
    
    运行结果
    start
    这是第一个yield语句的返回值
    这是第一个yield的下一行

    生成器表达式

    num = ('num%d' %i for i in range(10))#生成器表达式
    print(num)
    print((num.__next__()))
    print((num.__next__()))
    print(next(num))
    print(next(num))
    
    运行结果
    <generator object <genexpr> at 0x00AA34F0>
    num0
    num1
    num2
    num3
    

     列表解析和生成器表达式

    >>> l = [i for i in range(0,15)] #列表解析
    >>> print(l)
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
    >>> m = (i for i in range(0,15)) #生成器表达式
    >>> print(m)
    <generator object <genexpr> at 0x104b6f258>
    >>> for g in m:
    ...     print(g,end=', ')
    ... 
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,

    把列表解析的 [ ] 换成 ( ) 得到的就是生成器表达式

    列表解析和生成器表达式都是遍历的编程方式,但是生成器表达式更节省内存

    # 列表解析
    print([i for i in range(1,1000000)]) #内存占用过大,机器易卡死
    
    # 生成器表达式
    m = (i for i in range(1,100000000)) #几乎不占内存,运行的时间取决于cpu
    print(sum(m))

    python大部分内置函数都是使用迭代器协议访问对象。如 sum()

    sum(x**2 for x in range(4))
    
    sum([x ** 2 for x in range(4)]) #不用多此一举构造列表

    __next__()

    __next__()在生成器函数中可以运行至函数的下一个yield语句结束,并保留运行状态,返回yield语句定义的返回值

    next()

    next() 返回迭代器的下一个项目。

    内置函数 next() 本质就是在调用__next__()

    生成器的send用法

    def test():
        print('start')
        first = yield 1
        print('这是第一个yield的下一行', first)
        second = yield 2
        print('这是第二个yield的下一行', second)
        yield
    
    
    t = test() #产生生成器
    print(t.__next__()) #第一次运行只能使用next或者send(None)
    t.send('this is the first send value') #send具有next的功能,让程序运行到下一个yield语句结束
    t.send('this is the second send value')
    

      运行结果

    start
    1
    这是第一个yield的下一行 this is the first send value
    这是第二个yield的下一行 this is the second send value
    
    Process finished with exit code 0

    使用生成器实现并发

    import time
    
    def eat_grass(name):
        print('我是%s,我准备开始吃牧草了' % name)
        while True:
            grass = yield
            time.sleep(1)
            print('我开心地吃了第%s棵牧草' %grass)
    
    
    # name = 'cow'
    # eg = eat_grass(name)
    # eg.__next__()
    # eg.send('grass_number_01')
    # eg.send('grass_number_02')
    # eg.send('grass_number_03')
    
    def product_grass():
        name = 'cow'
        eg = eat_grass(name)
        eg.__next__()
        for i in range(1,10):
            time.sleep(1)
            eg.send('%d' %i)
    
    product_grass()

    总结

    以生成器函数为例进行总结:

    1、语法上和函数类似:差别在于常规函数只能使用一个return语句返回值,而生成器可以使用多个yield语句返回值

    2、自动实现迭代器协议:Python对于生成器自动实现了迭代器协议,所以可以调用 next,并在没有值可以next的时候,生成器自动产生StopIteration异常。还可以应用到迭代背景中(如for循环,sum函数)

    3、状态挂起:生成器使用yield语句返回一个值,此时yield语句挂起该生成器函数的状态,保留足够信息,以便之后的使用都是基于最新的状态

    优点:

    1、生成器的好处时延迟计算,一次返回一个结果。这样可以节省内存,提高运行效率。对大数据处理非常有用。

    见列表解析和生成器表达式的对比

    2、生成器可以提供代码的可读性

    注意:生成器只能遍历一次(例如生命只有一次,且年龄只会越来越大,不能返老还童)

    杂货铺

    列表解析

    l = [i for i in range(10)]#列表解析
    print(l)
    
    # 等价于
    l = []
    for i in range(10):
        l.append(i)
    print(l)
    

    三元表达式

    age = 20
    res = 'adult' if age>20 else 'child' #三元表达式
    print(res)
    
    # 等价于
    age = 20
    res = ''
    if age > 20:
        res = 'adult'
    else:
        res = 'child'
    print(res)

    装饰器

    概念和原则

    装饰即修饰,意指为其他函数添加新功能;

    装饰器的本质就是函数

    作用是为其他函数添加新功能,如计算该函数运行时长

    装饰器遵循原则:

    1.不修改被装饰函数的源代码(开放封闭原则)

    2.为被装饰函数添加新功能后,不能修改被修饰函数的调用方式

    装饰器的实现 = 高阶函数 + 函数嵌套 + 闭包

    高阶函数

    高阶函数 = 函数接收的参数是一个函数名 或 函数返回值包含函数名

      参数是一个函数名,可以为被修饰函数添加新功能

      返回值包含函数,可以不改变被修饰函数的调用方式

     1 # 高阶函数:参数包含函数名;或者返回值包含函数名
     2 
     3 import time
     4 
     5 def foo():
     6     time.sleep(1)
     7     print('this is from foo')
     8 
     9 def deco(func): #参数是一个函数名,可以为被修饰函数添加新功能
    10     starttime = time.time()
    11     func()
    12     endtime = time.time()
    13     print('运行%s函数共耗时%f' %(func,endtime-starttime))
    14     return func #返回值包含函数,可以不改变被修饰函数的调用方式
    15 
    16 foo = deco(foo)
    17 foo()
    18 # 缺陷:这里被修饰函数运行了两次,所以生成器不能只靠高阶函数!

    函数嵌套和闭包

    函数嵌套:在函数体中定义函数

    闭包:闭包就是能够读取其他函数内部变量的函数。在一个作用域里放入定义变量,相当于打了一个包

    def country(address):
        # name = 'alex'
        print('this is from country')
        def province():
            # name = 'blice'
            print('this is from province %s' %address)
    
    country('我的地址')

    装饰器的使用

     1 import time
     2 
     3 def deco(func):
     4     def qiantao():
     5         starttime = time.time()
     6         func()
     7         endtime = time.time()
     8         print('运行%s函数共耗时%f' %(func,endtime-starttime))
     9     return qiantao
    10 
    11 
    12 def foo():
    13     time.sleep(1)
    14     print('this is from foo')
    15 
    16 
    17 foo = deco(foo) #deco(foo)就是qiantao
    18 foo() #这里foo()相当于qiantao()
    不完善的装饰器
     1 import time
     2 
     3 def deco(func):
     4     def qiantao():
     5         starttime = time.time()
     6         res = func()
     7         endtime = time.time()
     8         print('运行%s函数共耗时%f' %(func,endtime-starttime))
     9         return res
    10     return qiantao
    11 
    12 
    13 @deco #就相当于在调用foo()之前执行 foo = deco(foo)
    14 def foo():
    15     time.sleep(1)
    16     print('this is from foo')
    17     return 'foo的返回值'
    18 
    19 
    20 # foo = deco(foo)
    21 res = foo()
    22 print(res)
    加上返回值和@
     1 import time
     2 
     3 def deco(func):
     4     def qiantao(*args, **kwargs): # args=(name, age)  kwargs={gender:male,}
     5         starttime = time.time()
     6         res = func(*args, **kwargs) #*(name,age)  **{gender:male}
     7         endtime = time.time()
     8         print('运行%s函数共耗时%f' %(func,endtime-starttime))
     9         return res
    10     return qiantao
    11 
    12 female = ''
    13 @deco #就相当于在调用foo()之前执行 foo = deco(foo)
    14 def foo(name, age, gender=female):
    15     time.sleep(1)
    16     print('this is from foo, =====> my name is %s ,age is %s, gender is %s' %(name, age,gender))
    17     return 'foo的返回值'
    18 
    19 res = foo('liming', 18, gender = '')
    20 print(res)
    修饰带参数的函数

    应用实例:

     1 user_list = [{'name':'alex', 'pwd':'123'},
     2             {'name':'belief', 'pwd':'123'},
     3             {'name':'a', 'pwd':'123'},]
     4 
     5 current_dic = {'username':'','login':False}
     6 
     7 def auth_fun(fun):
     8     def warp(*args, **kwargs):
     9         if current_dic['username'] and current_dic['login']:
    10             res = fun(*args, **kwargs)
    11             return res
    12         username = input('please input your name>')
    13         password = input('please input your password>')
    14         for user_dic in user_list:
    15             if username==user_dic['name'] and password==user_dic['pwd']:
    16                 current_dic['username'] = username
    17                 current_dic['login'] = True
    18                 res = fun(*args, **kwargs)
    19                 return res
    20         else:
    21             print('用户名或密码错误')
    22     return warp
    23 
    24 # index = auth_fun(index)
    25 @auth_fun
    26 def index():
    27     print('欢迎访问该网站')
    28 
    29 @auth_fun
    30 def home(name):
    31     print('这是的%s家页面' %name)
    32 
    33 @auth_fun
    34 def shopping_car(name):
    35     print('%s的购物车里有%s' %(name, '衣服'))
    36 
    37 index()
    38 # home('belief')
    39 # shopping_car('belief')
    无参装饰器
     1 user_list = [{'name':'alex', 'pwd':'123'},
     2             {'name':'belief', 'pwd':'123'},
     3             {'name':'a', 'pwd':'123'},]
     4 
     5 current_dic = {'username':'','login':False}
     6 
     7 def auth(type='qq'):
     8     '''根据用户的登录方式来进行登录操作'''
     9     def auth_fun(fun):
    10         def warp(*args, **kwargs):
    11             print('登录方式是%s' %type)
    12             if type=='qq':
    13                 if current_dic['username'] and current_dic['login']:
    14                     res = fun(*args, **kwargs)
    15                     return res
    16                 username = input('please input your name>')
    17                 password = input('please input your password>')
    18                 for user_dic in user_list:
    19                     if username==user_dic['name'] and password==user_dic['pwd']:
    20                         current_dic['username'] = username
    21                         current_dic['login'] = True
    22                         res = fun(*args, **kwargs)
    23                         return res
    24                 else:
    25                     print('用户名或密码错误')
    26             elif type=='wx':
    27                 print('打开微信扫一扫')
    28             else:
    29                 print('不支持其他登录方式,请使用qq或微信')
    30         return warp
    31     return auth_fun
    32 
    33 # index = auth_fun(index)
    34 @auth(type='qq') #autn(type='qq')就是auth_fun  @auth_fun相当于index=auth_fun(index), 即index = warp
    35 def index():
    36     print('欢迎访问该网站')
    37 
    38 @auth(type='wx')
    39 def home(name):
    40     print('这是的%s家页面' %name)
    41 
    42 @auth(type='sj')
    43 def shopping_car(name):
    44     print('%s的购物车里有%s' %(name, '衣服'))
    45 
    46 # index()
    47 home('belief')
    48 # shopping_car('belief')
    带参装饰器

     参考

    https://www.runoob.com/python3/python3-iterator-generator.html

    https://www.cnblogs.com/linhaifeng/articles/6140395.html

  • 相关阅读:
    使用 ASP.NET 2.0 ObjectDataSource 控件

    掌握 ASP.NET 之路:自定义实体类简介
    将 JavaScript 与 ASP.NET 2.0 配合使用
    C# 程序的通用结构
    实例化web service里类的实例
    Web服务枚举组件不可用 修复 (转载)
    09年初步学习计划
    Javascript return false的作用
    如何识别 SQL Server 的版本
  • 原文地址:https://www.cnblogs.com/dreamer-lin/p/11588261.html
Copyright © 2020-2023  润新知