zoukankan      html  css  js  c++  java
  • python 可迭代对象,迭代器和生成器,lambda表达式

    分页查找

    复制代码
    #5.随意写一个20行以上的文件(divmod)
    # 运行程序,先将内容读到内存中,用列表存储。
    # l = []
    # 提示:一共有多少页
    # 接收用户输入页码,每页5条,仅输出当页的内容
    def read_page(bk_list,n,endline=None):
        startline = 5 * (n-1)
        endline = endline+startline-1 if endline else startline + 4
        # print(startline,endline)
        for i in range(startline,endline+1):
            print(bk_list[i],end = '')
    
    def read_book(filename):
        f = open(filename)
        book_list = f.readlines()
        f.close()
        return book_list
    
    book_list = read_book('tmp_file')
    line_num = len(book_list)
    
    x,y = divmod(line_num,5)
    print(line_num,x,y)
    # if y:
    #     page = x+1
    # else:
    #     page = x
    page = x+1 if y else x
    print('一共有%s页'%page)
    while True:
        page_num = input('请输入您要阅读的页码 : ').strip()
        if page_num.isdigit():
            page_num = int(page_num)
            if page_num < page:
                read_page(book_list,page_num)
            elif page_num == page:
                read_page(book_list,page_num,y)
            else:
                print('您输入的内容有误')
    复制代码

    一、可迭代对象

    可以将某个数据集内的数据“一个挨着一个的取出来”,就叫做迭代

    可迭代协议

    假如我们自己写了一个数据类型,希望这个数据类型里的东西也可以使用for被一个一个的取出来,那我们就必须满足for的要求。这个要求就叫做“协议”。

    可以被迭代要满足的要求就叫做可迭代协议。可迭代协议的定义非常简单,就是内部实现了__iter__方法。

    可以被for循环的都是可迭代的,要想可迭代,内部必须有一个__iter__方法。

    复制代码
    l = [1,2,3,4,5]
    s = {1,2,3,4}
    #索引
    #for
    # for i in l:
    #     print(i)
    
    # for i in 50:
    #     print(i)
    
    #iterable  可迭代的
    #迭代
    
    #str
    #列表
    #tuple
    #set
    #dict
    
    #可迭代的 ——对应的标志  __iter__
    # print('__iter__' in dir([1,2,3]))  #判断一个变量是不是一个可迭代的
    复制代码

    二、迭代器

    接着分析,__iter__方法做了什么事情呢?

    print([1,2].__iter__())
    
    结果
    <list_iterator object at 0x1024784a8>
    复制代码
    #可迭代协议
    l = [1,2,3,4,5]
    # for i in l:
    #     print(i)
    # print(iter(l))   #内置函数
    l.__iter__()
    #iterator  #迭代器
    #iterator   iterable
    l_iterator = iter(l)
    # print(set(dir(l_iterator))-set(dir(l)))
    #__next__
    
    # iter({1,2,3})
    复制代码

    迭代器协议

    既什么叫“可迭代”之后,又一个历史新难题,什么叫“迭代器”?

    虽然我们不知道什么叫迭代器,但是我们现在已经有一个迭代器了,这个迭代器是一个列表的迭代器。

    我们来看看这个列表的迭代器比起列表来说实现了哪些新方法,这样就能揭开迭代器的神秘面纱了吧?

    可迭代协议——凡是可迭代的内部都有一个__iter__方法
    迭代器里既有iter方法,又有next方法 ——迭代器协议
    通过iter(o)得到的结果就是一个迭代器,
    复制代码
    '''
    dir([1,2].__iter__())是列表迭代器中实现的所有方法,dir([1,2])是列表中实现的所有方法,都是以列表的形式返回给我们的,为了看的更清楚,我们分别把他们转换成集合,
    然后取差集。
    '''
    #print(dir([1,2].__iter__()))
    #print(dir([1,2]))
    print(set(dir([1,2].__iter__()))-set(dir([1,2])))
    
    结果:
    {'__length_hint__', '__next__', '__setstate__'}
    复制代码

    我们看到在列表迭代器中多了三个方法,那么这三个方法都分别做了什么事呢?

    复制代码
    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__())
    复制代码

    在for循环中,就是在内部调用了__next__方法才能取到一个一个的值。

    那接下来我们就用迭代器的next方法来写一个不依赖for的遍历。

    复制代码
    l=[1,2,8,1,9,4]
    l_iterator=iter(l)
    print(l_iterator.__next__())
    print(l_iterator.__next__())
    while True:
        try:
            print(next(l_iterator))
        except:
            break
    复制代码

    判断是否是迭代器 和 可迭代对象的简便方法

    复制代码
    #迭代器 大部分都是在python的内部去使用的,我们直接拿来用就行了
    #迭代器:内置__iter__和__next__方法
    from collections import Iterable
    from collections import Iterator
    #判断是否是迭代器 和 可迭代对象的简便方法
    # s = 'abc'
    # print(isinstance(s,Iterable))
    # print(isinstance(s,Iterator))
    # print(isinstance(iter(s),Iterator))
    map_o = map(abs,[1,2,-3,4])
    #map_o = [1,2,3,4]
    print(isinstance(map_o,Iterable))
    print(isinstance(map_o,Iterator))
    复制代码

    range()

    复制代码
    #不管是一个迭代器还是一个可迭代对象,都可以使用for循环遍历
    #迭代器出现的原因 帮你节省内存
    from collections import Iterable
    from collections import Iterator
    a = range(100)
    print(isinstance(a,Iterable))
    print(isinstance(a,Iterator))
    复制代码

    为什么要有for循环

    基于上面讲的列表这一大堆遍历方式,聪明的你立马看除了端倪,于是你不知死活大声喊道,你这不逗我玩呢么,有了下标的访问方式,我可以这样遍历一个列表啊

    复制代码
    l=[1,2,3]
    
    index=0
    while index < len(l):
        print(l[index])
        index+=1
    
    #要毛线for循环,要毛线可迭代,要毛线迭代器
    复制代码

    for循环就是基于迭代器协议提供了一个统一的可以遍历所有对象的方法,即在遍历之前,先调用对象的__iter__方法将其转换成一个迭代器,然后使用迭代器协议去实现循环访问,这样所有的对象就都可以通过for循环来遍历了,

    复制代码
    #可迭代协议  :  内部实现了__iter__方法
    
    #迭代器协议  :  内部实现了__iter__ __next__方法
    
    #可迭代和迭代器的不同点 : 迭代器多实现了一个__next__方法
    
    #可迭代和迭代器的相同点 : 都可以用for循环
    
    #判断迭代器和可迭代的方法
    #第一种:判断内部是不是实现了 __next__
    #'__next__' in dir(o)
    
    #第二种:
    # from collections import Iterable  #可迭代
    # from collections import Iterator  #迭代器
    # isinstance(o,Iterable)
    # isinstance(o,Iterator)
    
    #迭代器的特点
    #可以用for循环
    #可以节省内存
    #你只能用一次  l = [1,2,3,4]
    复制代码

    初识生成器

    我们知道的迭代器有两种:一种是调用方法直接返回的,一种是可迭代对象通过执行iter方法得到的,迭代器有的好处是可以节省内存。

    如果在某些情况下,我们也需要节省内存,就只能自己写。我们自己写的这个能实现迭代器功能的东西就叫生成器。

     

    Python中提供的生成器:

    1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行

    2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表

     

    生成器Generator:

      本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现)

      特点:惰性运算,开发者自定义

    生成器函数

    一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束。

    
    
    复制代码
    import time
    def genrator_fun1():
        a = 1
        print('现在定义了a变量')
        yield a
        b = 2
        print('现在又定义了b变量')
        yield b
    
    g1 = genrator_fun1()
    print('g1 : ',g1)       #打印g1可以发现g1就是一个生成器
    print('-'*20)   #我是华丽的分割线
    print(next(g1))
    time.sleep(1)   #sleep一秒看清执行过程
    print(next(g1))
    复制代码

    生成器有什么好处呢?就是不会一下子在内存中生成太多数据

    假如我想让工厂给学生做校服,生产2000000件衣服,我和工厂一说,工厂应该是先答应下来,然后再去生产,我可以一件一件的要,也可以根据学生一批一批的找工厂拿。
    而不能是一说要生产2000000件衣服,工厂就先去做生产2000000件衣服,等回来做好了,学生都毕业了。。。

    复制代码
    初识生成器二
    
    def produce():
        """生产衣服"""
        for i in range(2000000):
            yield "生产了第%s件衣服"%i
    
    product_g = produce()
    print(product_g.__next__()) #要一件衣服
    print(product_g.__next__()) #再要一件衣服
    print(product_g.__next__()) #再要一件衣服
    num = 0
    for i in product_g:         #要一批衣服,比如5件
        print(i)
        num +=1
        if num == 5:
            break
    
    #到这里我们找工厂拿了8件衣服,我一共让我的生产函数(也就是produce生成器函数)生产2000000件衣服。
    #剩下的还有很多衣服,我们可以一直拿,也可以放着等想拿的时候再拿
    复制代码

    读取文件

    复制代码
    import time
    
    
    def tail(filename):
        f = open(filename)
        f.seek(0, 2) #从文件末尾算起
        while True:
            line = f.readline()  # 读取文件中新的文本行
            if not line:
                time.sleep(0.1)
                continue
            yield line
    
    tail_g = tail('tmp')
    for line in tail_g:
        print(line)
    复制代码

    计算移动平均值‘

    复制代码
    #计算移动平均值
    #7日平均年化收益
    #10,12,11  = (10+12+11)/3
    #total = 10 + 12  + 11
    #day = 1 + 1 + 1
    #avg = 10/1  22/2  33/3
    #       10    11    11
    
    def averager():
        total = 0
        day = 0
        avrage = 0
        while True:
            day_num  = yield avrage  #return avrage
            total += day_num
            day += 1
            avrage = total/day
    
    # avg = averager()
    # num = next(avg)  #激活生成器 avg.send(),什么都不send和next效果一样
    # print(avg.send(10))  #传值 next
    # print(avg.send(20))
    复制代码
    复制代码
    __author__ = 'Administrator'
    def wapper(fun):
        def inner(*args,**kwargs):
            g=fun(*args,**kwargs)
            next(g)
            return g
        return inner
    
    @wapper
    def aa():
        day=0
        total=0
        avrage =0
        while True:
            day_num=yield avrage
            total+=day_num
            day+=1
            avrage=total/day
    
    g=aa()
    print(g.send(20))
    复制代码

    yield from

    复制代码
    # __author__ = 'Administrator'
    # def fun():
    #     for i in "ab":
    #         yield i
    #         print("....")
    #         yield 1
    #
    # aa=fun()
    # while True:
    #     try:
    #         print(next(aa))
    #     except:
    #         break
    # print(next(aa))
    
    
    def fun():
        yield  from "abc"
        yield  from [1,2,4,5]
    
    aa=fun()
    print(next(aa))
    print(aa.__next__())
    print(next(aa))
    print(aa.__next__())
    
    while True:
        try:
            print(next(aa))
        except:
            break
    复制代码
    n = yield r    //yield,放弃本函数的执行,同时把r的值返回给调用者send()。 n的值就是send(n)的参数值

    r = c.send(n)    //r的值就是yield的参数值。
    复制代码
    next()让包含yield的函数(Generator)执行
    
     
    
    next()和send()在一定意义上作用是相似的,区别是send()可以传递值给yield表达式,而next()不能传递特定的值,只能传递None进去。因此,我们可以看做
    c.next() 和 c.send(None) 作用是一样的。
    
     
    
     
    
    第一次调用时,请使用next()语句或是send(None),不能使用send发送一个非None的值,否则会出错的,因为没有yield语句来接收这个值。
    
    1
    2
    3
    4
    5
    6
    def x():
            print 'bai wei'
            m = yield 5
            print m
            d = yield 12
            print 'reach here '
     
    
      
    c = x()
    c.next()     //启动generator
    c.next()     //给yield表达式传一个None值
    
    输出  
    
    bai wei
    None
    
     
    
    c = x()
        c.next()           /启动generator,运行到第一个yield处停止
    
        c.send('hi')     //从停止的yield处继续运行,并把值hi传过去
    
    输出
    
    bai wei
    hi
    
     
    
    c = x()
        c.send('hi')    //出错,generator还没启动
    
    输出
    
    Traceback (most recent call last):
    File "yield.py", line 36, in <module>
    c.send('hi')
    TypeError: can't send non-None value to a just-started generator
    复制代码
    复制代码
    5. send(msg) 与 next()的返回值
    
    send(msg) 和 next()是有返回值的,它们的返回值很特殊,返回的是下一个yield表达式的参数。比如yield 5,则返回 5 。到这里,是不是明白了一些什么东西?本文第一个例子中,通过for i in alist 遍历 Generator,其实是每次都调用了alist.Next(),而每次alist.Next()的返回值正是yield的参数,即我们开始认为被压进去的东东。我们再延续上面的例子:
    
    def h():
        print 'Wen Chuan',
        m = yield 5 
        print m
        d = yield 12
        print 'We are together!'
    
    c = h()
    m = c.next()  #m 获取了yield 5 的参数值 5
    d = c.send('Fighting!')  #d 获取了yield 12 的参数值12
    print 'We will never forget the date', m, '.', d
    输出结果:
    Wen Chuan Fighting!
    We will never forget the date 5 . 12
    复制代码

    列表推导式和生成器表达式

    复制代码
    #老男孩由于峰哥的强势加盟很快走上了上市之路,alex思来想去决定下几个鸡蛋来报答峰哥
    
    egg_list=['鸡蛋%s' %i for i in range(10)] #列表解析
    
    #峰哥瞅着alex下的一筐鸡蛋,捂住了鼻子,说了句:哥,你还是给我只母鸡吧,我自己回家下
    
    laomuji=('鸡蛋%s' %i for i in range(10))#生成器表达式
    print(laomuji)
    print(next(laomuji)) #next本质就是调用__next__
    print(laomuji.__next__())
    print(next(laomuji))
    复制代码
    复制代码
    # for i in range(100):
    #     print(i*i)
    
    # l =[i*i for i in range(100)]
    # print(l)
    
    # l = [{'name':'v','age':28},{'name':'v'}]
    # name_list = [dic['name'] for dic in l]
    # print(name_list)
    
    # l = [{'name':'v1','age':28},{'name':'v2'}]
    # name_list_generator = (dic['name'] for dic in l)
    # print(name_list_generator)
    # print(next(name_list_generator))
    # print(next(name_list_generator))
    
    # egg_list=['鸡蛋%s' %i for i in range(10)]
    # print(egg_list)
    
    # laomuji = ('鸡蛋%s' %i for i in range(1,11))
    # print(laomuji)
    # print(next(laomuji))
    # print(next(laomuji))
    
    # print(sum([1,2,3]))
    # print(sum(range(1,4)))
    
    def func():
        # yield from 'ABC'
        for i in 'ABC':
            yield i
    
    # g = func()
    # print(next(g))
    # print(next(g))
    
    for i in range(10):
        print(i)
    
    [i for i in range(10)]
    a = 10
    b=20
    if a >b :
        print(a)
    else:
        print(b)
    a if a>b else b
    复制代码

    一、复习

    迭代器:拥有__iter__,__next__;

    怎样从迭代器中取值:_next 和 for

    生成器:

    自己定义一个能实现迭代器功能的函数就是生成器

    生成器函数:带yield的函数,调用生成器函数,不会执行函数中的任何功能,只是返回一个生成器,调用next、时才执行函数中内容,遇到yield停止,并返回yield的值

    send方法就是向函数中传值,把值赋给yield对应的变量,同时还执行next方法

    一个要用send方法的生成器函数中至少要有两个yield、

    一个生成器函数中有多少个yield就可以调用多少个(next+send)方法、

    生成器的激活只能用next方法

    #2.生成器表达式
    #new_g = (i*i for i in range(100)) #new_g是一个生成器表达式

    二、作业

    读文件,找带关键字的行

    复制代码
    # 3.处理文件,用户指定要查找的文件和内容
    # 将文件中包含要查找内容的每一行都输出到屏幕
    
    def read(fname,str):
        with open(fname,encoding="utf-8")as f:
            l=f.readlines();
            for i in l:
                if str in i:
                    yield i
    
    aa=read("1","奥特曼")
    for i in aa:
        print(i)
    复制代码

    、进阶版本

    复制代码
    __author__ = 'Administrator'
    
    def init(fun):
        def inner(*args,**kwargs):
            g=fun(*args,**kwargs)
            next(g)
            return g
        return inner
    
    
    
    @init
    def read(g_read_line):
        while True:
            fname=yield
            str=yield
            with open(fname,encoding="utf-8")as f:
                l=f.readlines()
                for line in l:
                    if str in line:
                        g_read_line.send(line)
    
    @init
    def read_line():
        while True:
            line=yield
            if line:print(line)
    
    
    
    g_read=read(read_line())
    g_read.send("1")
    g_read.send("奥特曼")
    复制代码

    列表推倒式和生成器表达式

    复制代码
    new_l = []
    for i in range(100):
        new_l.append(i*i)
    
    #list(range(100))
    
    new_l = [i*i for i in range(100)]
    
    new_l = (i*i for i in range(100))  #生成器表达式
    复制代码

    生成器相关面试题

    复制代码
    # def demo():
    #     for i in range(4):
    #         yield i
    # g=demo()
    # g1=(i for i in g)
    # g2=(i for i in g1)
    # print(list(g2))
    # print(list(g1))
    
    def add(n,i):
        return n+i
    def test():
        for i in range(4):
            yield i
    g=test()
    for n in [1,10]:
        g=(add(n,i) for i in g)
    
    # # g=(add(n,i) for i in g)
    # n = 10
    # g=(add(n,i) for i in (add(n,i) for i in g))
    # print(list(g))
    
    # for i in [1,2]:
    #     pass
    #
    # print(i)
    
    
    import os
    
    def init(func):   #预激活生成器的一个装饰器
        def wrapper(*args,**kwargs):
            g=func(*args,**kwargs)  #func是一个生成器函数,返回的g是一个生成器
            next(g)   #预激活生成器
            return g #返回激活后的生成器g
        return wrapper
    
    @init    #list_files = init(list_files) == wrapper
    def list_files(target): #target = opener_g
        while 1:
            dir_to_search=yield
            for top_dir,dir,files in os.walk(dir_to_search): #os.walk   (路径,文件夹,文件)
                for file in files:  #从文件列表中获取一个一个的文件
                    target.send(os.path.join(top_dir,file))  #把文件的绝对路径传给了opener_g
    @init    #opener = init(opener)  ==  wrapper
    def opener(target): #target = cat_g
        while 1:
            file=yield   #拿到了一个文件的路径
            fn=open(file,encoding='utf-8') #打开文件获取了一个文件句柄
            target.send((file,fn))  #cat_g发送了一个文件的路径和句柄
    @init  #cat = init(cat) == wrapper
    def cat(target):  #target = grep_g
        while 1:
            file,fn=yield  #文件路径和文件的句柄
            for line in fn:
                target.send((file,line))  #文件路径,文件中的一行
    @init  #grep = init(grep) == wrapper
    def grep(pattern,target):  #要搜索的关键字,printer_g
        lst = []
        while 1:
            file,line=yield  #文件的路径和每一行
            if pattern in line and file not in lst: #判断关键字是否在当前行
                lst.append(file)
                target.send(file) #printer_g.send文件路径
    @init  #printer = init(printer) == wrapper
    def printer():
        while 1:
            file=yield  #获取一个文件路径
            if file:
                print(file)  #打印文件的路径:文件里包含了要搜索的关键字
    
    g=list_files(opener(cat(grep('python',printer()))))
    # g=list_files(opener(cat(grep('python',printer_g))))
    # g=list_files(opener(cat(grep_g)))
    # g=list_files(opener(catg)))
    # g=list_files(opener_g)
    g.send('D:Python代码文件存放目录S6day18')
    
    #用户给一个路径和关键字
    #可以从一个文件路径中找到所有包含关键字的文件
    复制代码

    二、匿名函数、

    匿名函数 简单的需要用函数去解决的问题 匿名函数的函数体 只有一行也叫lambda表达式

    复制代码
    # cal2 = lambda n : n*n
    # ret = cal2(20)
    # print(ret)
    
    # def add(x,y):return x+y
    # add2 = lambda x,y : x+y
    # ret = add2(1,2)
    # print(ret)
    复制代码
    print(max(dic))
    func = lambda k:dic[k]
    print(max(dic,key = func))
    print(max(dic,key=lambda k:dic[k]))
    l = [1,2,3,4]
    d=lambda x:x*x
    print(list(map(d,l)))
    l = [10,11,8,12]
    # def func(x):
    #     return x>10
    # print(list(filter(func,l)))
    # print(list(filter(lambda x:x>10,l)))
    复制代码
    #现有两个元组(('a'),('b')),(('c'),('d')),请使用python中匿名函数生成列表[{'a':'c'},{'b':'d'}]
    t1 = (('a'),('b'))
    t2 = (('c'),('d'))
    print(list(zip(t1,t2)))
    c=lambda t:{t[0]:t[1]}
    d=map(c,zip(t1,t2))
    print (list(d))
    
    
    d=list(map(lambda z:{z[0],z[1]},zip(t1,t2)))
    print(d)
    复制代码
    l=[i for i in range(100) if i%3==0]
    print(l)
    print(len(l))
    # names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
    #          ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
    # print([name for lst in names for name in lst if name.count('e') >=2])
    复制代码
    mcase = {'a': 10, 'b': 34}
    for i in mcase:
        print(i)
    
    [i for i in mcase]
    print({key:key+'1' for key in mcase})
    print({key:mcase[key] for key in mcase})
    print({mcase[key]:key for key in mcase})
    
    mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
    mcase_frequency = {k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0) for k in mcase}
    print(mcase_frequency)
    复制代码
  • 相关阅读:
    自底向上的归并排序 .[转]
    分治法寻找数组最大的两个数和最小的两个数
    分治法求最大最小值
    数字移动【转】
    NRF24L01无线模块的使用
    对钙铀云母放射强度的测量
    自制用于放置钙铀云母的铅盒
    Arduino从DHT11读取温湿度数据并显示在1602LCD
    β particle, α particle, γ ray, ionization chamber
    Arduino通过I2C(PCF8574T)驱动1602LCD
  • 原文地址:https://www.cnblogs.com/Xanderzyl/p/10589589.html
Copyright © 2011-2022 走看看