Python18_可迭代对象与迭代器、生成器

可迭代对象

def:可以直接作用于for循环的对象统称为可迭代对象(Iterable)。可以用isinstance()去判断一个对象是否是iterable对象。迭代:即在上一次的基础上进行下一次的操作

可以直接作用于for的数据类型一般分两种

  1. 集合数据类型,如list,tuple,dic,set,string

  2. 是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(),函数作用如下:
  1. 检查对象是否实现了__iter__方法,如果实现了,就会调用他,,返回一个迭代器
  2. 如果没有实现__iter__方法,但是实现了__getitem__方法,python对创建一个迭代器,尝试按顺序(从0开始)获取元素。
  3. 如果,前两者都没有,则会抛出TypeError异常。
  4. 将迭代器的元素读取完毕之后,会触发一个StopIteration异常
  • 迭代器规定了两个方法:
  1. __iter__返回迭代器本身
  2. __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)

方式二:

  1. 只要python函数的定义中有yield关键字,该函数就是生成器,调用该生成器函数时,会返回一个生成器对象。即生成器函数是用于生成生成器的。
  2. 生成器本质上是迭代器,不过一个是读取数据,一个是产生数据,即返回一个yield关键字后表达式的值
  3. 一般函数是使用return返回,生成器是由yield返回

ps:

  1. 因为生成器对象最后会抛出StopIteration的异常,所以建议结合try...except使用
  2. 如果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)

完成多任务消耗的资源:协程 < 线程 < 进程

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,198评论 6 514
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,334评论 3 398
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,643评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,495评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,502评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,156评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,743评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,659评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,200评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,282评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,424评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,107评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,789评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,264评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,390评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,798评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,435评论 2 359

推荐阅读更多精彩内容