迭代器和生成器

一、列表生成式

将一个列表每个元素加1
1.普通方法

a = [1,2,3,4,5,6,7,8,9]
for	index , i  in a:
	a[index] = i + 1
print(a)

2.map方法

a = [1,2,3,4,5,6,7,8,9]
a = map(lambda x:x+1,a)
print(a)       # 此时a变成了一个对象(<map object at 0x0000021AFF275390>),要想将a中元素取出需要用到for循环或者next()方法

# next方法,每次只能取一个值
print(next(a))

# for循环取出a中的每个值
for i in a:
    print(i)

3.列表生成式

# 列表生成式生成的是一个列表,可以直接打印出来
a = [1,2,3,4,5,6,7,8,9]
a = [ i + 1 for i in a]
print(a)

使用列表生成式,可以将所有要生成的元素全部生成出来,但是如果需要生成的数据量比较大时,则会大量占用你内存空间。由于受内存空间限制,列表容量是有限的。

二、生成器

一般的函数在执行完毕之后会返回一个值然后退出,但是生成器函数会自动挂起,然后重新拾起继续执行,他会利用yield关键字关起函数,给调用者返回一个值,同时保留了当前的足够多的状态,可以使函数继续执行。
1、在列表生成式上修改来创建生成器

上面的列表生成式由于一次性将列表元素全部生成出来,在需要调用前面几个元素时,则显得此法浪费空间,此时就需要用到生成器(generator )。要创建一个生成器,只需要将上面的列表生成式做稍微改动即可获得一个生成器:

a = [1,2,3,4,5,6,7,8,9]
a = ( i + 1 for i in a)        # 此时a即为一个生成器,其与列表生成式区别在于列表生成器是中括号,生成器是小括号;此时a为一个                   									                                                                             对象( <generator object <genexpr> at 0x0000021C1E1D5F48>)                                                                                                                                                               
print(next(a))
for i in a:
	print(i)

用列表生成式生成的 生成器不可使用切片操作

2、用函数创建生成器
如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,可以用函数来实现。

# 斐波那契数列
def fib(max):
	n = 0
	a = 0
	b = 1
	while n < max:
		print(b)
		c = a + b
		a = b
		b = c
	return
	
# 将上面斐波那契转换成生成器
def fib(max):
	n = 0
	a = 0
	b = 1
	while n < max:
		yeild b            # 将函数执行过程冻结在这一步,当下次调用时将其唤醒,并且把b的值返回给next
		c = a + b
		a = b
		b = c
	return
f = fib(5)
print(f)                   # 打印结果为:<generator object fiv at 0x00000253C2775F48>
print(next(f))             # 如果用print(next(fib(5)))方式调用则结果不一样,会一直打印b的初始值
print(next(f))

3.生成器的send方法

def fiv():
    print('ok1')
    x = 10
    print(x)
    x = yield 3
    print(x)
    yield 5


f = fiv()
f1 = next(f)
print(f1)

f2 = f.send(666)
print(f2)

        
# 在用send方法向生成器里面传值时,send()的括号中必须填值。第一次调用send进行传值时不能发送一个非空的值,即第一次调用send必须是此种形式send(None),否则会报错(TypeError: can't send non-None value to a just-started generator),因为生成器just-started generator,是没有Python yield语句来接收这个值的。用send传参时对于 x = yield 3 等式的理解是,在调用send时等式左边的  x 是等于send传进去的参数,右边的是上次next调用时的返回值

def fib(max):
    n = 0
    while n < max:
        a = yield n
        n = n+1
        print(a)

f = fib(3)
print(f.__next__())   # 结果是0
print(f.__next__())   # 由于未传参a = None,所以print(a)打印的是None,print(f.__next__())打印是1


生成器调用既可以用next方法也可以用for循环方法,区别在于:

1.用next方法调用时,当调用次数超过生成器生成的元素个数时,会报StopIteration错误

2.for循环调用时会将生成器生成元素全部打印出来,程序不会报StopIteration错误

3.用next方法调用时每next调用一下就可以按照调用顺序生成一个元素,当停止next调用时,生成器元素会停留在当前停 止的位置,下次再调用next方法时则从之前停止位置继续调用,并且在next停止后使用for循环时,for循环打印元素从next停止的位置开始打印直到结束,因此生成器调用不可逆。

4.send方法和next方法类似,都是将函数唤醒并接着上次暂停的地方开始执行。send()和next()的区别就在于send可传递参数给yield表达式,这时候传递的参数就会作为yield表达式的值,而yield的参数是返回给调用者的值,也就是说send可以强行修改上一个yield表达式值。

5.send()和next()都有返回值,他们的返回值是当前迭代遇到的yield的时候,yield后面表达式的值,其实就是当前迭代yield后面的参数。

6.第一次调用时候必须先next()或send(),否则会报错,send后之所以为None是因为这时候没有上一个yield,所以也可以认为next()等同于send(None)

三、 迭代器

1.可迭代对象
可以将某个数据集内的数据“一个挨着一个的取出来”,就叫做迭代。
而在python中可以直接用for循环的数据类型可分为两类:
一类是集合数据类型:如list(列表),tuple(元组),dict(字典),str(字符串),set(集合)等
一类是generator,包括生成器和带yield的generator function
这些都可以直接作用于for循环的对象统称为可迭代对象:Itarable
可以使用 isinstance() 判断一个对象是不是可迭代对象。

from collections import Iterable
a = isinstance([], Iterable)
b = isinstance({}, Iterable)
c = isinstance('abc', Iterable)
d = isinstance((x for x in range(10)), Iterable)
e = isinstance(100, Iterable)
print(a)     # 打印结果为True
print(b) 	 # 打印结果为True
print(c) 	 # 打印结果为True
print(d) 	 # 打印结果为True
print(e) 	 # 打印结果为False


而生成器不但可以作用于for循环,还可以被next()不断调用并且返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。
2.迭代器
可以被next()函数调用不断返回值下一个值的对象称为迭代器(Iterator)
可以使用isinstance()判断一个对象是否是Iterator对象 :

form collection import Iterator
a = isinstance((x for x in range(10)), Iterable)
b = isinstance([], Iterable)
c = isinstance({}, Iterable)
d = isinstance('abc', Iterable)

print(a)     # 打印结果为True
print(b) 	 # 打印结果为False
print(c) 	 # 打印结果为False
print(d) 	 # 打印结果为False


生成器都是迭代器对象,但是list(列表),dict(字典),str(字符串)等不是迭代器

这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数 据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

将列表,字典和字符串转换成迭代器:

# 将列表转换成迭代器
iter_l = [1,2,3,4,5,6].__iter__()   
#获取迭代器中元素的长度
print(iter_l.__length_hint__())
#根据索引值指定从哪里开始迭代
print('*',iter_l.__setstate__(4))
#一个一个的取值
print('**',iter_l.__next__())
print('***',iter_l.__next__())

小结

1.凡是可作用于for循环的对象都是可迭代(Iterable)类型;

2.凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列

3.集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值