迭代器或者生成器不会提前去申请内存,等用的时候才会产生内存,所以只是提供了产生元素的方法
1、range函数
py3—>range返回是range对象,py2—>range返回的是list
若list是个很大的数据的情况下,py3不会直接返回一个大数据的list(占内存),只是一个对象
range的三个参数:起始数据、结束数据(不包含)、步进(两个数据的间隔)
如range(-4) —>[],只有一个参数的情况下是结束数据
range的方向看第三个参数的正负,正:左往右,负:右往左
2、可迭代,迭代器
from collections import Iterable,Iterator
可迭代对象:实现了迭代器协议的对象,在产生的类中定义了
__iter__()
方法
Iterable:
可迭代对象 判断是否是可迭代对象if isinstance(a,Iterable)
Iterator:
迭代器 判断是否是迭代器if isinstance(a,Iterator)
字符串、列表、元组、字典都是可迭代对象
特性:迭代器只能迭代一次,迭代完里面的元素就没了,list或者其他容器,如果元素很多的情况下,是直接申请内容空间把元素放到内存里面,会消耗大量内存,而迭代器是生成一个迭代器对象,在用到元素的时候才会去申请内容空间放置这个元素
iter(a)
:将可迭代对象a转换成迭代器,用到元素的时候才生成元素,不会消耗大量内存
可迭代对象不是迭代器,构造的迭代器—>参数数一个可迭代对象
自定义可迭代对象:
class MyIter():
def __init__(self,num):
self.num = num
def __iter__(self):
return iter(self.num.split(' '))
a_tier = MyIter('a b c d e f g')
for i in a_tier:
print(i)
**总结:**迭代器是可迭代对象,可迭代对象不一定是迭代器
**迭代器优点:**不依赖与索引,迭代器对于内存的优化,同一时间只产生一个数据,而list同一时间是全部的元素数据
**迭代器缺点:**只能往后取值,而且是一次性的,每个元素只能迭代一次,不能统计迭代器的元素个数和迭代器长度
**取出迭代器的元素next():print(next(迭代器名))
多次迭代则多次调用next()取元素,若next()
的时候超出了迭代器的元素个数的限制会报错(StopIteration)
还可以通过for循环取出迭代器元素
**迭代器:**能被next(xxx对象)
方法调用,有__iter__()
和__next__()
方法
__iter__()
:返回迭代器本身
__next__()
:返回下一个元素,定义了这个方法后就能通过next()获取元素
next()
:函数里面有定义__next__
就能通过这个方法获取元素,又或者可以说定义了__next__
的对象就是一个迭代器
自定义迭代器:主要是实现__next__
,和__iter__
方法
# 自定义一个迭代器,实现类似与range函数的功能,产生 start 到end之间的数,两边闭合。
class MyRangeItertor:
def __init__(self,start,end,step=1):
self.start = start
self.end =end
self.step =step
def __iter__(self):
return self #返回迭代器本身
def __next__(self):
if self.start <= self.end:
num = self.start
self.start +=self.step
return num
else:
raise StopIteration
a = MyRangeItertor(1,3)
for i in a:
print(i)
dict.items()
:返回字典所有的数据项,按照数据原本的数据类型返回
enumerate()
:返回可迭代对象的索引和元素,如:for i in enumerate([1,2,3]):print(i)
3、推导式
列表推导式[]:
[x for i in range(5)]
—>返回list
[x for i in range(5) if x > 2]
字典推导式{}:{key:value for key,value in enumerate(range(10))}
集合推导式{}:{key for key in range(10) if key%2==0}
4、生成器
from collections import Generator
isinstance(a,Generator)
:判断是否是生成器
生成器(generator)
:python有一种“一边遍历一遍计算值”的方式产生可迭代对象,这就是生成器。生成器是一种特殊的迭代器,返回结果用yield关键字在运行时产生,前期不占内存,解决列表推导式占额外内存的问题。可以通过send()函数给生成器发信号
构造生成器的方式一:生成器表达式,如下
b_gen = (x for x in range(10))
:生成器表达式,不是元组,没有元组推导式,生成器也是一种迭代器,生成器的元素只能被遍历一次,拿了就没了
可通过next()
一个一个取(超出限制的异常不会内部处理),可通过for循环取(自动处理异常)
close()
:生成器的方法,生成器对象执行这个方法后,就不能获取元素了,相当于没元素了,在用next()
,则会报错StopIteration
send(a)
:可将a传递给yield表达式,然后赋值给一个变量,可以通过将这个变量作为某种命令,改变生成器的执行方式,停止或者改变
构造生成器的方式二:yield
关键字+函数定义生成器
def fun(num):
while num>2:
#造成的现象是:取一个元素,代码会暂停,把元素for循环出来在执行下面代码
yield num
#相当于把num抛出到函数外,函数代码暂停,保留上次执行的状态
#把抛出的num传递给获取值的对象
num -=1
#然后在通过for循环取生成器的元素:
for i in fun(6):
print(i)
#生成器的斐波那契数列
def fib():
start,end = 0,1
while True:
yield end
start,end = end,end+start
from itertools import islice
#从一个迭代器里面获取其中的 x-xx项 组成一个有限序迭代器对象
print(list(islice(fib, 0, 10)))
send()
:生成器的方法,可传递一个参数给yield的变量,相当于给生成器传递一个指令
说明yield之后,下次执行是在yield这里开始执行,只是不会再返回
def fun_C():
a = 1,step = 1
while True:
r = yield a
if isinstance(r,int):
step = r
a+=step
f = fnu_c()
next(f)#打印f
next(f)#打印 None 和 2 None是r的值,则表明再次执行时从yield开始的
#f.send(None)#如send(),第一次要一定传None 相当于next(f)
f.send(100)#将100赋值给了 yield a 然后赋值给了r
next(f)#打印f 就变成了102
5、切片
a_list[a,b,c]
:a切片的起始位置,b是结束位置(不包含),c是步长(正数方向是左往右,负数是右往左)
6、从itertools中导入一些常用迭代器。
count
为寄数器, count(start=1,step=3)
, 没有上限
from itertools import count
#产生一个无限的迭代器,去3开始步长为2的数,可以无限取
counter = count(3,2)
next(counter)
next(counter)
next(counter)
from itertools import islice
#获取一个无限的迭代器的90-100的10个组成的有限序列
print(list(islice(counter , 90, 100)))
islice
从一个无限序列中提取出一个有限序列。
islice(obj, start,stop,step)
举例:斐波拉契数列重新实现
class Fib:
def __init__(self):
self.left = 0
self.current = 1
def __iter__(self):
return self
def __next__(self):
value = self.current
self.left, self.current = self.current, self.current + self.left
return value
# 获取斐波拉契数列迭代器的前10个元素。
from itertools import islice
f = Fib()
#从一个迭代器里面获取其中的 x-xx项 组成一个有限序迭代器对象
print(list(islice(f, 0, 10)))
cycle
将一个有限序列变为一个无限序列。
week_days = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六']
cycle_week = cycle(week_days)
for i in range(10):
print(next(cycle_week))
dis
:**方法可以直接看一段代码的执行过程
import dis
#看字符串里面的代码执行的过程
dis.dis('for i in range(10):print(i)')