重修课程day12(函数之迭代器和生成器)

Posted 方杰0410

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重修课程day12(函数之迭代器和生成器)相关的知识,希望对你有一定的参考价值。

面向过程编程

 面向过程编程是一种以过程为中心的编程思想,分析出解决问题的步骤,然后用函数把这些步骤一步一步实现。面向过程编程,数据和对数据的操作是分离的。

写程序时:要先想功能,分步实现

os模块中walk输出目录中文件路径,os.walk() 方法用于通过在目录树中游走输出在目录中的文件名,向上或者向下。

Send可以传多个值,但是必须是元组类型。面向过程的编程思想像流水线,代码简洁,体系结构。

#应用:grep -rl \'root\' /etc
import os
def init(func):
    def wrapper(*args,**kwargs):
        g=func(*args,**kwargs)
        next(g)
        return g
    return wrapper
#阶段一:递归地找文件的绝对路径,把路径发给阶段二
@init
def search(target):
    \'search file abspath\'
    while True:
        start_path=yield
        g = os.walk(start_path)
        for par_dir, _, files in g:
            # print(par_dir,files)
            for file in files:
                file_path = r\'%s\\%s\' % (par_dir, file)
                target.send(file_path)
#阶段二:收到文件路径,打开文件获取获取对象,把文件对象发给阶段三
@init
def opener(target):
    \'get file obj: f=open(filepath)\'
    while True:
        file_path=yield
        with open(file_path,encoding=\'utf-8\') as f:
            target.send((file_path,f))

#阶段三:收到文件对象,for循环读取文件的每一行内容,把每一行内容发给阶段四
@init
def cat(target):
    \'read file\'
    while True:
        filepath,f=yield
        for line in f:
            res=target.send((filepath,line))
            if res:
                break

#阶段四:收到一行内容,判断root是否在这一行中,如果在,则把文件名发给阶段五
@init
def grep(target,pattern):
    \'grep function\'
    tag=False
    while True:
        filepath,line=yield tag #target.send((filepath,line))
        tag=False
        if pattern in line:
            target.send(filepath)
            tag=True
#阶段五:收到文件名,打印结果
@init
def printer():
    \'print function\'
    while True:
        filename=yield
        print(filename)

start_path1=r\'C:\\Users\\Administrator\\PycharmProjects\\python5期\\a\'
start_path2=r\'C:\\Users\\Administrator\\PycharmProjects\\python5期\\a\\b\'
g=search(opener(cat(grep(printer(),\'root\'))))

print(g)
# g.send(start_path1)
g.send(start_path2)

#应用:grep -rl \'root\' /etc
View Code

一  迭代器

    迭代的意思:类似于循环每一次重复的过程就被称为迭代的过程,提供迭代方法的容器称为迭代器。

    1.迭代器协议是指:迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,知道所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。

    2.可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法),对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代

    3.协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象。

 集合的目的:去重,关系运算。

# a={1,2,3,4,5,6,7}
# b={41,52,5,26,7,4,2,9,}
# print(a-b)

  isinstance:判断数据的类型,还可以判断是否可迭代。

  iterable:形容词  可迭代的:from collections import Iterable:用来检测一个对象是否可以迭代。

# from collections import Iterable
# print(isinstance(\'fadda\',Iterable))

  dir:打印一种数据类型的方法 

print(dir(\'asdasf\'))

 什么叫做可迭代对象:操作的对象下面有__iter__()方法的就是可迭代对象。

 什么叫做迭代器:操作的对象下面不光有__iter__()方法的,还有__next__()方法的就是迭代器,迭代器是不用关心值得索引状态的

  iterator:迭代器:实现了能从其中一个一个的 取值出来。

lst_iterator=[1,2,3,4,5].__iter__()
print(lst_iterator.__next__())

 一切可以用for循环的基本数据类型都是可迭代对象,而不是迭代器。for循环的可以是一个可迭代对象,也可以是一个迭代器。而for循环自动为可迭代对象调用__iter__()方法。

for i in [1,2,3,4,5,6]:
    print(i)

  文件本质就是一个迭代器。range()就是一个可迭代对象,可迭代对象有:字符串,列表,元组等等。

__iter__():将一个可迭代对象转化成一个迭代器

__next__():读取迭代器的内容,一次只能读取一行或者一个内容

# with open(\'a.txt\',encoding=\'utf-8\')as f:
#     print(f.__next__())
#     print(f.__next__())
#     print(f.__next__())
#     print(f.__next__())
#     print(f.__next__())
# 
# b=[41,52,5,26,7,4,2,9,]
# bb=b.__iter__()
# print(bb.__next__())
# print(bb.__next__())
# print(bb.__next__())
# print(bb.__next__())
# print(bb.__next__())

 迭代器取到没有值的时候就会报错。报的是StopIteration。

迭代的概念:重复的过程称为迭代,每次重复即一次迭代,并且每次迭代的结果是下一次迭代的初始值

 迭代器的好处:1 能够对python中的基本数据类型进行统一的遍历,不需要关系每一个值是什么。

        2 惰性预算:可以节省内存。

# for i in range(1,1000):
#     print(i)
#     if i == 100:
#         break

迭代器的坏处:

无法获取长度
一次性的,只能往后走,不能往前退

迭代器的创建:1 天生就是一个迭代器,比如说:文件句柄

       2 后天转换成迭代器:迭代器=可迭代对象.__iter__()

二 生成器

 什么是生成器(Gerator):生成器本质就是一个迭代器,生成器就是自己写出来的迭代器。

 什么是生成器函数:函数里有yield的关键字就是一个生成器函数。

# def iter_1():
#     print(111)
#     yield 222
#     print(333)
#     yield 444
# 
# iter_2=iter_1()
# print(iter_2.__next__())
# print(iter_2.__next__())

  yield:意思和return差不多,但是yiled可以定位当前取值的位置,就是在next取值过后,就定位到取到的那个值的位置,等再次使用next取值的时候,就从第一次取值过后定位的那个位置开始取值,一个next对应一个yield。

   from:yield下的一个方法,可以简化生成器函数。用了from,就可以不用for循环。

# def foo():
#     for i in [1,2,3,4,5,6,7]:
#         yield i
# for i in foo():
#     print(i)

# def foo():
#     yield from [1,2,3,4,5,6,7]
# for i in foo():
#     print(i)

 生成器函数在调用的时候返回来的就是一个生成器,不会执行生成器函数里面的内容。

# def iter_1():
#     print(111)
#     yield 222
#     print(333)
#     yield 444
# 
# iter_2=iter_1()
# print(iter_2)

 而生成器和迭代器运行的顺序是只能往前取值,而不能够回头。

  生成器的用法:1 有几个next,就取几个yield的返回值,不能超过yiled的范围。

         2 直接for循环取值:for 变量名 in 生成器名。

         3 其他数据类型进行强转,list(数据类型对象)返回一个列表,里面装着生成器的所有内容。

for i in range(1,10):
    print(i)
print(list(range(10,20)))

  注意:1 生成器函数取值必须先转换成生成器,在进行next取值。

     2 生成器的内容只能取一次,不能重复,只能向前取值;不能后退,直到取完为止。

惰性运算:生成器里面的内容不获取,不生成。

   为函数封装好了__iter__和__next__方法,把函数的执行结果做成了迭代器

 遵循迭代器的取值方式obj.__next__(),触发的函数的执行,函数暂停与再继续的状态都是由yield保存的    

协同程序,就是可以运行的独立函数调用,保留函数的局部变量等数据,让函数可以暂停或者挂起。并且在需要的时候会从上次执行的后面第一个地方继续执行。

yield表达式

yield关键字用来定义生成器(Generator),其具体功能是可以当return使用,从函数里返回一个值,不同之处是用yield返回之后,可以让函数从上回yield返回的

地点继续执行。也就是说,yield返回函数,交给调用者一个返回值,然后再“瞬移”回去,让函数继续运行, 直到吓一跳yield语句再返回一个新的值,能多次重复使用。

使用yield返回后,调用者实际得到的是一个迭代器对象,迭代器的值就是返回值,而调用该迭代器的next()方法会导致该函数恢复yield语句的执行环境继续往下跑,

直到遇到下一个yield为止,如果遇不到yield,就会抛出异常表示迭代结束。

 

1. 包含yield的函数

假如你看到某个函数包含了yield,这意味着这个函数已经是一个Generator,它的执行会和其他普通的函数有很多不同。

调用函数之后,print 语句并没有执行!这就是yield,那么,如何让print 语句执行呢?这就是后面要讨论的问题,通过后面的讨论和学习,就会明白yield的工作原理了。

2. yield是一个表达式

Python2.5以前,Python yield是一个语句,但现在2.5中,yield是一个表达式(Expression)

假如,m=yield 5。表达式(yield 5)的返回值将赋值给m,所以,认为 m = 5 是错误的。那么如何获取(yield 5)的返回值呢?需要用到后面要介绍的send(msg)方法。

3. 透过next()语句看原理

现在,我们来揭晓yield的工作原理。我们知道,函数被调用后并没有执行,因为它有yield表达式,因此,我们通过next()语句让它执行。next()语句将恢复Generator执行,

并直到下一个yield表达式处。

当我们再次调用print(next(*))时,会继续执行,直到找到下一个yield表达式。由于后面没有yield了,因此会拋出异常:

4. send(msg) 与 next(),

了解了next()如何让包含yield的函数执行后,我们再来看另外一个非常重要的函数send(msg)。其实next()和send()在一定意义上作用是相似的,区别是send()可以传递yield表达式的

值进去,而next(*)不能传递特定的值,只能传递None进去。因此,我们可以看做next(*) 和 *.send(None) 作用是一样的。

     

需要提醒的是,第一次调用时,请使用next()语句或是send(None),不能使用send发送一个非None的值,否则会出错的,因为没有Python yield语句来接收这个值。

5. send(msg) 与 next()的返回值

send(msg) 和 next()是有返回值的,它们的返回值很特殊,返回的是下一个yield表达式的参数。

其实是每次都调用了alist.Next(),而每次alist.Next()的返回值正是yield的参数,即我们开始认为被压进去的东东。

6 可以为wield表达式创建一个装饰器,就是在多个表达式开始执行前不用每个都输入next(*) 和 *.send(None),为next(*) 和 *.send(None)创建一个装饰器,在每个wield表达式

函数名称前加上@装饰器的名称。装饰器为此提供了方便,可以多个一起使用。

三 文件补充小知识

文件下seek()传的值:0:光标的相对位置;1:当前光标的位置;2:将光标一道最后一个一个位置。

# def aa(bb,dd):
#     with open(bb,\'w\',encoding=\'utf-8\')as f :
#         f.write(dd)
# aa(\'a.txt\',\'afdfd\\nfadsa\')
# def tail(cc):
#     f=open(cc,encoding=\'utf-8\')
#     f.seek(0,2)
#     while True:
#         line=f.readline()
#         if not line:
#             continue
#         yield line
# tail_g=tail(\'a.txt\')
# for line in tail_g:
#     print(line,end=\'\')
#

以上是关于重修课程day12(函数之迭代器和生成器)的主要内容,如果未能解决你的问题,请参考以下文章

重修课程day26(面向对象6之反射,内置函数和常用模块)

重修课程day15(函数之内置函数一)

重修课程day32(网络编程六之进程三)

重修课程day24(面向对象5之封装)

重修课程day6(python基础4之集合和文件操作)

重修课程day31(网络编程五之进程二)