• 7.15迭代器、生成器及常用内置方法


    迭代器

    1.什么是迭代器

      迭代:更新换代(重复)的过程,每次的迭代都必须基于上一次的结果

    n = 0
    while True:
        print(n)
    # 不算迭代,因为只是简单的重复
    
    
    s = 'hello'
    n = 0
    while n < len(s):
        print(s[n])
        n += 1
    # 重复 + 每次迭代都是基于上一次的结果而来的才是迭代

      迭代器:迭代取值的工具

    2.为什么要用

      迭代器给我们提供了一种不依赖于索引取值的方法

    3.可迭代对象

    n = 1
    f = 1.1
    s = 'hello'
    l = [1,2,3,4]
    t = (1,2,3,4)
    s1 = {1,2,3,4}
    d = {'name':'francis'}
    f1 = open('xxx.txt','w',encoding='utf-8')
    res = s.__iter__()  # 相当于res = iter(s)
    res1 = l.__iter__()  # res1 = iter(l)
    res2 = t.__iter__()  # res2 = iter(t)
    res3 = s1.__iter__()  # res3 = iter(s1)
    res4 = d.__iter__()  # res4 = iter(d)
    res5 = f1.__iter__()  # res5 = iter(f1)
    print(res,res1,res2,res3,res4,res5)
    print(f1)
    View Code

    (1)只有内置方法中有 __iter__ 方法的都叫做可迭代对象

    (2)可迭代对象执行内置的 __iter__ 方法得到就是该对象的迭代器对象

    (3)文件对象执行内置的 __iter__ 之后还是本身,没有任何变化,文件对象本身就是迭代器对象

      所以基本数据类型中是可迭代对象的有:字符串(str)、列表(list)、元组(tuple)、字典(dict)、集合(set)、文件对象

    补充:

      针对双下线开头双下划线结尾的方法(__iter__),推荐读:双下+方法名

    迭代器取值

    1.迭代器对象及迭代器取值(__next__)

    l = [1,2,3,4]
    iter_l = l.__iter__()  # 把可迭代对象l转成迭代器对象
    print(iter_l.__next__())  # 迭代器取值,调用__next__,而且迭代器对象的取值必须用__next__
    print(iter_l.__next__())  # 一次取一个
    print(iter_l.__next__())
    print(iter_l.__next__())
    print(iter_l.__next__())  # 如果取完了,直接报错,报错StopIteration

      (1)内置方法中既有 __iter__ 方法,也有 __next__ 方法的就是迭代器对象

      (2)只有 __iter__ 方法的是可迭代对象,迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象

      (3)迭代器取值的特点:只能往后依次取,不能回退

    """
    问:__iter__方法就是用来帮我们生成迭代器对象的
    而文件对象本身就是迭代器对象,为什么还内置中还有__iter__方法?
    """
    f1 = open('xxx.txt','r',encoding='utf-8')  # 本来就是迭代器对象
    iter_f = f1.__iter__()
    print(iter_f is f1)  # 比较内存地址是否相同
    
    l = [1,2,3,4]
    iter_l = l.__iter__()  # 生成迭代器对象
    iter_2 = iter_l.__iter__().__iter__().__iter__()
    print(iter_2 is iter_l)  # 比较内存地址是否相同

      结论:迭代器对象无论执行多少次 __iter__ 方法得到的还是迭代器对象本身

    2.异常处理

    迭代器取完值了会报错

    l = [1,2,3,4]
    iter_l = l.__iter__()  # 把可迭代对象l转成迭代器对象
    print(iter_l.__next__())  # 迭代器取值,调用__next__,而且迭代器对象的取值必须用__next__
    print(iter_l.__next__())  # 一次取一个
    print(iter_l.__next__())
    print(iter_l.__next__())
    print(iter_l.__next__())  # 如果取完了,直接报错,报错StopIteration

    如何让它不报错,使用try + except

    l = [1,2,3,4]
    iter_l = l.__iter__()  # 把可迭代对象l转成迭代器对象
    while True:
        try:  # 标识下面的代码可能会出错
            print(iter_l.__next__())
        except StopIteration:  # 如果出现StopIteration这个错误,就走下面这一步
            print('迭代器取完值了')
            break

    for循环的本质

    d = {'name':'francis','password':'123','hobby':'read'}
    for i in d:  # for循环后面的in跟的必须是一个可迭代对象
        print(i)

    1.for循环内部的本质

      (1)将in后面的对象调用 __iter__ 转换成迭代器对象

      (2)调用 __next__ 对这个迭代器迭代取值

      (3)内部有异常捕获StopIteration,当 __next__ 报了这个错,就自动结束循环

    d = {'name':'francis','password':'123','hobby':'read'}
    iter_d = d.__iter__()  # 将in后面的对象调用 __iter__ 转换成迭代器对象
    while True:
        try:  # 标识下面的代码可能会出错
            print(iter_d.__next__())  # 调用 __next__ 对这个迭代器迭代取值
        except StopIteration:  # 如果出现StopIteration这个错误,就走下面这一步
            print('迭代器取完值了')
            break  # 结束循环

    2.迭代取值的优缺点

    优点:

      (1)不依赖于索引取值

      (2)内存中永远只占一份空间,不会导致内存溢出(一次只取一个值)

    缺点:

      (1)不能够获取指定的元素

      (2)取完元素之后会报StopIteration这个错

    生成器

    1.本质

      用户自定义的迭代器,本质就是迭代器

    2.如何自定义迭代器

      自定义函数 + yield的方法

    def func():
        print('第一个值')
        yield 111  # yield后面跟的值就是调用迭代器__next__方法时你能得到的值
        print('第二个值')
        yield 222
        print('第三个值')
        yield 333
        print('第四个值')
        yield 444,555,666  # yield既可以返回一个值也可以返回多个值,并且多个值也是按照元组的形式返回
    g = func()  # 生成器初始化:函数内如果有yield关键字,那么加括号执行函数的时候并不会触发函数体代码的运行,而是将函数变成迭代器
    print(g)
    print(g.__next__())  # 代码运行到第一个yield后就暂停
    print(g.__next__())  # 直到再次取值,一次取一个值
    print(g.__next__())
    print(g.__next__())
    print(g.__next__())  # 取完值就报错

    3.如何自定义一个函数,实现内置函数range的功能

    for i in range(1,10,2):
        print(i)

      range其实就是一个可迭代对象

    实现:

    def my_range(start,end,step=1):
        while start < end:
            yield start  # 第一个值小于第二个值就返回第一个值
            start += step  # 然后第一个值加上步长再判断
    
    for i in my_range(1,100,2):  # 步长可通过传参修改
        print(i)

    yield表达式形式(了解即可)

    def people(name):
        print('%s 准备开吃'%name)
        while True:
            food = yield  # 代码运行到第一个yield后就暂停
            print('%s 吃了 %s'%(name,food))
    g = people('francis')  # 当函数内有yield关键字的时候,调用该函数不会执行函数体代码,而是将函数变成生成器
    g.__next__()  # 必须先将代码运行至yield,才能够为其传值
    g.send('蔬菜')  # 给yield左边的变量传参,并且触发__next__方法
    g.send('')

    1.yield

      (1)帮你提供了一种自定义生成器的方式

      (2)会帮你将函数的运行状态暂停住

      (3)可以返回值

    2.yield与return之间的异同点

    相同点:

      都可以返回值,并且都可以返回多个

    不同点:

      (1)yield可以返回多次值,而return只能返回一次值,而且函数立即结束

      (2)yield还可以接受外部传入的值(send)

    生成器表达式

      跟列表、字典、集合生成式一样,不同的是外面用括号

    res = (i for i in range(1,10) if i != 4)  # 生成器表达式
    print(res)
    print(res.__next__())
    print(res.__next__())
    print(res.__next__())
    print(res.__next__())

    1.如何统计文件中的字符个数

    (1)第一种

    f = open('xxx.txt','r',encoding='utf-8')
    data = f.read()  # 把文件内容一次性读完
    print(len(data))  # 统计字符个数
    f.close()

      该方法占内存:如果文件过大,一次性读完太占内存

    (2)第二种:for循环

    with open('xxx.txt','r',encoding='utf-8') as f:
        n = 0
        for line in f:  # 一次只读取文件的一行内容
            n += len(line)  # 统计每一行的字符数并依次相加
        print(n)

      该方法节省内存:内存中每次只会有一行内容

    (3)第三种:生成器

    g = (len(line) for line in f)  # 生成器表达式
    print(sum(g))  # 求和(内部for循环)

      把生成器g中的值for循环出来然后相加

    2.面试题

    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)
    res=list(g)
    
    # A. res=[10,11,12,13]
    # B. res=[11,12,13,14]
    # C. res=[20,21,22,23]
    # D. res=[21,22,23,24]
    # 选出正确答案

    解答:

    def add(n,i):
        return n+i
    def test():
        for i in range(4):
            yield i
    g=test()  # 不执行函数体代码,而是将函数变成生成器,所以不会执行函数里的for循环
    for n in [1,10]:  # for循环两次,第一次n=1,第二次n=10
        g=(add(n,i) for i in g)
        '''
        第一次for循环时第二个g为生成器test(),也就是g1=(add(n,i) for i in test())
        第二次for循环时第二个g为上次循环得到的生成器g1,也就是g2=(add(n,i) for i in (add(n,i) for i in test()))
        两次循环结束,得到的是两个生成器,都没有执行生成器中的for循环
        '''
    res=list(g)
    '''
    其实求的就是生成器g2的里面的值,此时求值时才会执行各生成器中的for循环,g2时n=10
    把n=10代入g2生成器中也就是:
    g2 = (add(10,i) for i in (add(10,i) for i in range(4)))
    res = list(g2)
    print(res)
    答案是C
    '''
    # A. res=[10,11,12,13]
    # B. res=[11,12,13,14]
    # C. res=[20,21,22,23]  正确答案
    # D. res=[21,22,23,24]
    View Code

    常用内置方法

    1.abs:求绝对值

    print(abs(-11.11))  # 求绝对值

    2.all、any:

    l = [0,1,2]
    print(all(l))  # 只要有一个元素为False就返回False
    print(any(l))  # 只要有一个元素为True就返回True

    3.bool:判断数值的bool值为True还是False

    print(bool(1))
    print(bool(0))

    4.bytes:转成二进制类型

    s = 'hello'
    print(bytes(s,encoding='utf-8'))

    5.callable:判断是否为可调用对象(加括号执行代码)

    l = [1,2,3]
    def index():
        pass
    print(callable(l))
    print(callable(index))

    6.chr、ord

    print(chr(97))  # 将数字转换成ascii码表对应的字符
    print(ord('a'))  # 将字符按照ascii表转成对应的数字

    7.dir:会返回当前对象名称空间里面所有的名字(当前对象可调用的方法)

    l = [1,2,3]
    print(dir(l))

    8.divmod:分页器

    print(divmod(105,10))  # 第一个值除以第二个值,返回(结果,余数)
    #可以利用方法来求总页数
    total_num,more = divmod(901,10)  #909为数据总数,10为每页显示多少数据
    if more:
        total_num += 1
    print('总页数:',total_num)

    9.enumerate:枚举

    l = ['a','b']
    for i,j in enumerate(l,1):  # 依次给数据编号,默认从0开始,可以添加参数从参数开始编号
        print(i,j)

    10.eval、exec

    s = "print('hello baby~')"
    s1 ='''
    x = 1
    y = 2
    print(x + y)
    '''
    eval(s)
    exec(s)
    exec(s1)  # 支持逻辑代码
    eval(s1)  # 报错,eval不支持逻辑代码,只支持一些简单的python代码

    11.globals、locals

    def index():
        username = '我是局部名称空间里面的username'
        print(locals())  # 当前语句在哪个位置,就会返回哪个位置所存储的所有的名字
        print(globals())  # 无论在哪,查看的都是全局名称空间
    index()

    12.help:查看函数注释

    def login():
        """
        注释
        :return:
        """
    print(help(login))

    13.isinstance

    n = 1
    print(type(n))
    print(isinstance(n,list))  # 判断对象是否属于某个数据类型,传入你认为这个对象是什么类型

    14.pow:次方运算

    print(pow(2,3))  # 后一个值是前一个值几次方,计算结果,此处是求2的3次方的结果

    15.round:求大概值,四舍五入

    print(round(3.4))
    print(round(3.5))

    面向过程编程  

    1.面向过程编程:

      就类似于设计一条流水线

    好处:

      将复杂的问题流程化,从而简单化

    坏处:

      可扩展性较差,一旦需要修改,整体都会受到影响

    2.本质:

      就相当于按我们人类正常的思维模式,一步一步的解决问题

  • 相关阅读:
    在vue中添加实时时间
    在three中使用图片作为材质,并将材质流动起来
    在three.js中创建一个小球并且小球在外部添加辉光
    自动获取linux系统的iso镜像文件
    this指向的相关问题
    vue基础-01
    svn和git的简单使用命令和步骤
    您只能在 HTML 输出中使用 document.write。如果您在文档加载后使用该方法,会覆盖整个文档
    关于toLocaleDateString的坑
    JSON的介绍与细节
  • 原文地址:https://www.cnblogs.com/francis1/p/11192070.html
Copyright © 2020-2023  润新知