赞
踩
迭代是访问集合元素的一种方式,迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束,迭代器只能往前不会后退。
我们已经知道可以对list、tuple、str等类型的数据使用 for...in... 的循环语法从其中依次拿到数据进行使用,我们把这样的过程称为遍历,也叫迭代。
判断一个东西是否可以迭代,导入 Iterable,当是Iterable的子类时,则可以迭代。使用isinstance()方法
那类对象是否可以迭代,创建一个 demo 测试一下
和数字类型一样运行报错 'Classmate' object is not iterable
那如果想让一个类对象可以被迭代呢?我们只要在类里实现 __iter__方法即可
此时程序依然报错,但是报错内容为,iter() 方法返回 None
我们使用 Iterable 验证一下,结果很明显,说明实现了 iter方法的类对象是 可迭代的对象
此时我们解决 Iter() 方法 返回值报错问题:
当使用for 循环迭代一个类对象时,
1.首先判断该对象是否可以被迭代(即,对象的类中是否实现了__Iter__方法)
2.继续判断 __Iter__方法是否返回一个迭代器(返回一个类对象,实现该对象的类中必须有__Iter__和 __next__ 方法 )
3.然后调用iter函数(不是__iter__方法),得到迭代器(对象),继续调用next函数,此函数会调用迭代器的__next__方法,
__next__方法return的值,就是 for temp in 对象 的 temp值 ,每for循环一次,调用一次__next__方法
使用 Iterator 验证 classmate_iterator 对象,返回True,说明 对象的一个迭代器
运行结果:
使用next方法迭代对应的迭代器对象,即返回迭代器 __next__方法的返回值
运行结果:
上述验证使用的init()方法和next()方法都会被for循环调用,此时我们为了将可迭代对象的对应属性传递到迭代器,
在__init__方法中传递self
运行结果:
最后我们优化上述代码,将可迭代对象和迭代器放在同一个类中,即同时实现__iter__和__next__方法
- #!/usr/bin/env python
- # _*_ coding:utf-8 _*_
-
-
- class Classmate(object):
- def __init__(self):
- self.names = list()
- self.current_num = 0 # 计数器
-
- def add(self, name):
- self.names.append(name)
-
- def __iter__(self):
- """可迭代的对象,即可以使用 for,必须实现 Iter() 方法"""
- return self
-
- def __next__(self):
- if self.current_num < len(self.names): # 防止越界
- ret = self.names[self.current_num]
- self.current_num += 1
- return ret
- # 当if条件不成立时,默认继续返回None值,使用StopIteration 结束迭代
- else:
- raise StopIteration
-
-
- classmate = Classmate()
- classmate.add("老王")
- classmate.add("王二")
- classmate.add("张三")
-
- for name in classmate:
- print(name)
如果一个对象是迭代器,它一定是可迭代,对应的,如果一个对象是可迭代的,那它不一定是迭代器。
迭代器的作用:
下面两种代码均可实现得到前10位的斐波那契数列
而更推荐使用第二种迭代器,因为代码占用内存更小,保存的是生成的方式,需要时在使用,而不是保存生成的结果
- nums = list()
-
- a = 0
- b = 1
- i = 0
- while i < 10:
- nums.append(a)
- a, b = b, a+b
- i += 1
-
-
- for num in nums:
- print(num)
- class Fibonacci(object):
- def __init__(self, all_num):
- self.all_num = all_num
- self.current_num = 0
- self.a = 0
- self.b = 1
-
- def __iter__(self):
- return self
-
- def __next__(self):
- if self.current_num < self.all_num:
- ret = self.a
-
- self.a, self.b = self.b, self.a+self.b
- self.current_num += 1
-
- return ret
- else:
- raise StopIteration
-
-
- fibo = Fibonacci(10)
-
-
- for num in fibo:
- print(num)
我们进行数据类型转换时,也是利用迭代器的过程
生成一个空列表,然后进行迭代打印输出
特殊的迭代器(不需要iter next方法)
创建生成器
使用列表推导式生成一个列表,如下图
将中括号换成小括号,nums为一个对象,这就是生成器
此时我们就可以迭代nums
两者比较,一种是返回数据,一种是返回生成数据的方式,即节省空间。
第二种创建生成器的方式(常用)
定义一个函数,当 yield 存在时, 函数则为 生成器
如下例生成前三位的斐波那契数列数列,函数中存在 yield,此时 函数为一个生成器的模板,调用函数的返回值 obj 是一个 生成器。
运行过程:
使用for循环迭代 obj ,循环时,从上往下依次执行,当执行到 yield 语句是,将 yield 后的 a 的值赋予 num,执行暂停,打印输出 num,结束第一次循环,第二次循环,直接从 yield语句下方执行,当再次碰到 yield语句时,重复上述过程,直到运行结束
- #!/usr/bin/env python
- # _*_ coding:utf-8 _*_
-
- def create_num(all_num):
- print("----1---")
- # a = 0
- # b = 1
- a, b = 0, 1
- current_num = 0
- while current_num < all_num:
- print("----2---")
- # print(a)
- yield a # 如果一个函数中有yield语句,那么这个就不在是函数,而是一个生成器的模板
- print("----3---")
- a, b = b, a+b
- current_num += 1
- print("----4---")
-
- # 如果在调用create_num的时候,发现这个函数中有yield那么此时,不是调用函数,而是创建一个生成器对象
- obj = create_num(3)
-
- for num in obj:
- print(num)
-
运行结果:
当生成器模板有 return 返回值时,需要在结束异常时,使用value 得到 返回值
- def create_num(all_num):
- # a = 0
- # b = 1
- a, b = 0, 1
- current_num = 0
- while current_num < all_num:
- # print(a)
- yield a # 如果一个函数中有yield语句,那么这个就不在是函数,而是一个生成器的模板
- a, b = b, a+b
- current_num += 1
- return "ok...."
-
- obj = create_num(5)
-
- while True:
- try:
- ret = next(obj)
- print(ret)
- except Exception as ret: # 捕获异常
- print(ret.value) # 对应 return 的值
- break
我们除了可以使用next()函数来唤醒生成器继续执行外,还可以使用send()函数来唤醒执行,使用send()函数的一个好处是可以唤醒的同时向断点处传入一个附加数据。
send()方法yield a 的值对应 send()方法传递的参数
结果:
不可以将send放在第一次,程序会报错,因为程序从最上面执行,没有值接收传递的参数
总结一下,迭代器的一大特点,可以用极小的空间、代码生成想要的数据
生成器的一大特点,yield --------- 可以让一个函数暂停执行,并且每次调用的值依旧保存,下次调用可以继续使用
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。