• Python中的迭代
  • 发布于 1周前
  • 36 热度
    0 评论
  • VOVO1
  • 0 粉丝 36 篇博客
  •   
写代码的时候,经常写些列表生成式。有的时候想,这种方法是否高效呢?我们学了那么久的生成器,怎么就没派上用场呢?

在天朝,啥都要排个队,买房摇号排队,医院挂号排队,幼儿园上学报名排队,吃饭排队,超市买单排队,安检排队。。。
那些排队的群众,可以说是一个可以迭代的对象

Python中,只要是可迭代对象,都可以迭代。

那么,如何判断一个对象是不是可迭代对象呢?方法是通过collections中的Iterable类型判断。
 from collections import Iterable 
 isinstance('123',Iterable)
True
 isinstance([1,2,3],Iterable)
True
isinstance(123,Iterable)
False
假如现在有个需求,看列表 [0,1,2,3,4,5,6,7,8,9],要求你把列表里面的每个值加1,你怎么实现呢?

方法一(简单)
for index,i in enumerate(info):
    info[index] +=1
print(info)
方法二(一般):
info = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
a = map(lambda x:x+1,info)
print(a)
for i in a:
    print(i)
方法三(高级):
info = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
a = [i+1 for i in range(10)]
print(a)
    enumerate()函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。

什么是生成器?
  通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的,而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

python中的生成器
  要创建一个generator,有很多种方法,第一种方法很简单,只有把一个列表生成式的[]中括号改为()小括号,就创建一个generator
#列表生成式
lis = [x*x for x in range(10)]
print(lis)

#生成器
generator_ex = (x*x for x in range(10))
print(generator_ex)   #<generator object <genexpr>
print(list(generator_ex)) #转换成了列表

    迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合

特点:

访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容
不能随机访问集合中的某个值 ,只能从头到尾依次访问
访问到一半时不能往回退
便于循环比较大的数据集合,节省内存
这个很好理解:就是我排队的时候,中途要离开一下,然后叫朋友帮我排着,回来的时候我继续接替朋友排的位置。我就不需要重新开始从末尾开始排。我的位置到队伍最前是能计算得到的。比如医生叫号,不管你前后一位是张三还是李四,他只要叫:”下一位”。
#生成器
generator_ex = (x*x for x in range(10))
print(next(generator_ex))
print(next(generator_ex))

s = "Today is a happy day"
y=iter(s)
print(next(y))
z = s.__iter__()
print(next(z))
大家可以看到,generator保存的是算法,每次调用next(generaotr_ex)就计算出他的下一个元素的值,直到计算出最后一个元素,没有更多的元素时,抛出StopIteration的错误,而且上面这样不断调用是一个不好的习惯,正确的方法是使用for循环,因为generator也是可迭代对象:

#生成器
generator_ex = (x*x for x in range(10))
for i in generator_ex:
    print(i)

yield
yield的功能类似于return,但是不同之处在于它返回的是生成器。
yield是python内部的一个关键字,内部实现支持了迭代器协议,同时yield内部是一个状态机,维护着挂起和继续的状态,yield关键字返回的就是一个生成器。
def out_money(totle):
    while totle > 0:
        totle -= 1
        yield 1                             # yield 返回一个值

ATM = out_money(3)
print("取到钱 %s 万" % ATM.__next__())
print("花掉花掉!")
print("取到钱 %s 万" % ATM.__next__())
print("花掉花掉!")
print("取到钱 %s 万" % ATM.__next__())
print("花掉花掉!")
print("取到钱 %s 万" % ATM.__next__())    # 到这时钱就取没了,再取就报错了
print("取到钱 %s 万" % ATM.__next__())
结果:

取到钱 1 万
花掉花掉!
取到钱 1 万
花掉花掉!
取到钱 1 万
花掉花掉!
Traceback (most recent call last):
  File "/Users/anderson/class/scripts_demo/appium_demo/third/mydict.py", line 123, in <module>
    print("取到钱 %s 万" % ATM.__next__())    # 到这时钱就取没了,再取就报错了
StopIteration
    这个yield的主要效果呢,就是可以使函数中断,并保存中断状态,中断后,代码可以继续往下执行,过一段时间还可以再重新调用这个函数,从上次yield的下一句开始执行。

这个可以这么理解,就是我排队排了好久,然后要去洗手间。让朋友来替我排队,不管这个队伍怎么变化,我回来只要找到我朋友,排在朋友替我排的位置。就不需要苦逼得从头开始排,next就到来我前面一个人的位置。

    一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。

    yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。
用户评论