可迭代对象
def:可以直接作用于for循环的对象统称为可迭代对象(Iterable)。可以用isinstance()去判断一个对象是否是iterable对象。迭代:即在上一次的基础上进行下一次的操作
可以直接作用于for的数据类型一般分两种
集合数据类型,如list,tuple,dic,set,string
是generator,包括生成器和带yield的generator funcion
from collections import Iterable
print(isinstance([],Iterable))
print(isinstance(" ",Iterable))
print(isinstance((),Iterable))
print(isinstance({},Iterable))
print(isinstance((x for x in range(10)),Iterable)) #此处isinstance的第一个参数为迭代器
print(isinstance(1, Iterable)) #输出False
迭代器
- 迭代器,当需要迭代,会自动调用系统函数iter(),函数作用如下:
- 检查对象是否实现了
__iter__
方法,如果实现了,就会调用他,,返回一个迭代器 - 如果没有实现
__iter__
方法,但是实现了__getitem__
方法,python对创建一个迭代器,尝试按顺序(从0开始)获取元素。 - 如果,前两者都没有,则会抛出TypeError异常。
- 将迭代器的元素读取完毕之后,会触发一个StopIteration异常
- 迭代器规定了两个方法:
-
__iter__
返回迭代器本身 -
__next__
返回下一个元素
不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出一个StopIteration错误,则表示无法继续返回下一个值
可以被next()调用并不断返回下一个值的对象称为迭代器(Iterator对象)
格式:next(迭代器)
可以使用 isinstance()函数判断一个对象是否是Iterator对象
from collections import Iterator
print(isinstance([],Iterator))
print(isinstance(" ",Iterator))
print(isinstance((),Iterator))
print(isinstance({},Iterator))
print(isinstance((x for x in range(10)),Iterator)) #只有这个是True,因为第一个为迭代器
print(isinstance(1, Iterator)) #输出False
转为Iterator对象
使用iter()方法,可以将列表,元组,字符串,字典转为迭代器;iter方法必须返回对象的引用
a = iter([1,2,3,4]) #iter()方法如果传两个参数,则第一个必须为函数,会重复调用第一个参数(即函数),直到返回第二个参数为止
print(next(a), next(a))
#ps:Iterator对象占用的空间一定比原来的列表或元组等可迭代对象占用的空间小
迭代器应用
#input方法默认以换行符结束输入
endstr = "end"
str = ""
for line in iter(input,endstr):
str += (line + "\n")
print(str)
import reprlib
class Sentences:
def __init__(self, text):
self.text = text
self.words = text.split()
def __getitem__(self, item):
return self.words(item)
def __len__(self):
return len(self.words)
def __repr__(self):
return "Sentence:%s" % reprlib.repr(self.text) # 显示的时候,如果内容太长,中间用省略号表示
s = Sentences("This is an aple, and that is a orange")
for word in s:
print(word)
重写__next__
与__iter__
class CountDown:
def __init__(self,step):
self.step = step
def __next__(self):
if self.step <= 0:
raise StopIteration
self.step -= 1
return self.step
def __iter__(self): # iter方法必须返回一个对象的引用,这个对象必须有iter方法和next方法
return self
for ele in CountDown(10):
print(ele)
# for temp in xxx_obj时究竟做了什么:
# 1. 判断xxx_obj是否是可以迭代(里面是否有iter方法)
# 2. 在第一步成立的情况下,调用iter函数得到xxx_obj对象iter的返回值
# 3. iter方法的返回值是一个迭代器
# 4. for的时候就调用iter返回对象的next方法,for一次调用next一次,将next返回值给temp
元组转为列表的过程:
a = (11, 22, 33)
b = list(a)
# 1. 先找到a里面的迭代器
# 2. 通过next取a里面的值,然后把值添加到列表里面
生成器
生成器就是一种特殊的迭代器
方式一:
a = (x for x in range(10))
#a即是一个生成器,可以用来迭代
for x in a:
print(x)
方式二:
- 只要python函数的定义中有yield关键字,该函数就是生成器,调用该生成器函数时,会返回一个生成器对象。即生成器函数是用于生成生成器的。
- 生成器本质上是迭代器,不过一个是读取数据,一个是产生数据,即返回一个yield关键字后表达式的值
- 一般函数是使用return返回,生成器是由yield返回
ps:
- 因为生成器对象最后会抛出StopIteration的异常,所以建议结合try...except使用
- 如果a是一个生成器,则也可以使用a.__next__()调用,功能与next(a)一模一样
def fibnacci():
a,b = 0,1
while True:
yield b #b即是要返回的值,每次调用next,就在此处停止,并返回此时的b
a,b = b, a+b
return "haha" # 因为是生成器,且此处是死循环,所以此句无用,但是如果while有终止的时候
# 那么当循环已经达到终止条件,但是还在next或用for遍历时,就会抛出StopIteration错误
# 当使用except Exception as e时,e.value就是其返回值,即:“haha”
f = fibnacci() #f即是一个生成器。在调用一个方法的时候,如果它有yield,那么就不是调用该方法
# 而是创建一个生成器对象
for _ in range(100):
print(next(f), end = " ")
send
对生成器传值,传入的值作为yield 表达式的整体返回值,然后使生成器往下走一圈,停在下一个即将赋值时
ps:next是使生成器停在yield处,send是使生成器停在即将赋值时,yiled之后
send的作用:可以根据send的值来调整生成器中下次获得的值
def gen():
i = 0
while i < 5:
temp = yield i
print(temp)
i += 1
f = gen()
print(next(f)) #第一次停在了yield处,所以这里输入0
f.send("haha") #从上次的yield处开始执行,即进行赋值操作,将haha作为上次yiled i的整体赋给temp,故此处输出haha,但是应注意,此后又走了一圈,执行了一次i += 1,然后停在了赋值处
print(next(f)) #因为上次停在了赋值处,所以这里会先输出一个None,然后i += 1后再停在yield处,即输出2
f.send("biubiu~") #此处输出biubiu~
输出结果
caution:send必须在next之后,因为send是作为yield的整体返回值的,所以必须先用next调一下yield。如果非要先send,则send的参数必须为None
零碎
在没有第三个变量的前提下如何交换两个变量的值
#对于python
a,b = b,a
#其他语言(python也适用):加减
a = a + b
b = a - b
a = a - b
#其他语言(python也适用):异或
略
用生成器完成多任务(协程)
ps:多任务的分类:协程、进程、线程。协程的速度相对较快
使用了send()方法的生成器不再称为生成器,而可以称为协程
def test1():
while True:
print("---1---")
yield None
def test2():
while True:
print("---2---")
yield None
t1 = test1()
t2 = test2()
while True:
next(t1)
next(t2)
完成多任务消耗的资源:协程 < 线程 < 进程