• 迭代器,for循环本质,生成器,常用内置方法,面向过程编程


    一、迭代器

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

     迭代器:迭代取值的工具

    2、迭代器给你提供了一种不依赖于索引取值的方式

    3、可以迭代取值的对象:字符串,列表,元组,字典,集合等

    4、可迭代对象:内置有__iter__方法的都叫做可迭代对象

    5、针对双下划线开头和结尾的方法:读法是双下+方法名

    6、基本数据类型中,是可迭代对象的有:str、list、tuple、dict、set

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

    7、迭代器对象

      ①、可迭代对象执行内置的__iter__方法得到的就是该对象的迭代器对象

      ②、迭代器对象内置有__iter__方法和__next__方法

      ③、迭代器对象一定是可迭代对象

      ④、迭代器对象只能通过执行__next__方法取到迭代器内的值,执行一次返回一个,执行次数超过迭代器内值的个数时报错(StopIteration)

      ⑤、迭代器取值的特点:只能往后依次取,不能后退

      ⑥、迭代器对象无论执行多少次__iter__方法得到的还是迭代器对象本身(******)

    8、异常处理

    while True:
        try:
            print(iter_d.__next__())
        except StopIteration:
            print("老母猪生不动了")
            break

      该程序会一直执行try后面的代码,当遇到except后面的错误时,执行except后面的程序,一般是终止循环,如果遇到的不是except后面的错误,则正常报错。

    9、文件对象本身就是迭代器对象,为什么还内置有__iter__方法?

      这就要谈到for循环的本质了。

    二、for循环的本质

    1、for循环只能用于可迭代对象

    2、for循环内部的本质:

      ①、将in后面的对象调用__iter__转换成迭代器对象

      ②、调用__next__迭代取值

      ③、内部有异常捕获StopIteration,当__next__报这个错,自动结束循环

    3、可迭代对象:内置有__iter__方法的

    迭代器对象:即内置有__iter__也内置有__next__方法

    4、迭代取值的优缺点:

    优点:

      ①、不依赖于索引取值

      ②、内存中永远只会占一份空间,不会导致内存溢出

    缺点:

      ①、不能够获取指定的元素

      ②、取完之后会报StopIteration错误

    5、for循环只能循环可迭代对象,而迭代器对象也是可迭代对象,当for循环一个迭代器对象时,会先对其调用__iter__方法,如果迭代器对象没有内置__iter__方法,就会报错。

    三、生成器

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

    2、形式

    def func():
        print("first")
        yield 666
        print("second")
        yield 777
        print("third")
        yield 888
    

    3、函数定义体内部有yield关键字,函数名加括号次并不会执行函数体代码,而是会将其变成一个生成器,也叫作生成器初始化,生成器与迭代器一样通过__next__取值,而取到的值就是yield后面传的值,可以传一个值,也可以传多个值,传多个值会以元组的形式存储。生成器执行__next__方法时,遇到yield就会暂停,并将yield后面的值传出来。并且生成器执行__next__方法次数超过yield传值的个数时,也会报StopIteration错误。

    4、例子,自己写一个range函数,简单模仿内置range函数

    def my_range(start, end, step):
        while start < end:
            yield start
            start += step
    
    for j in my_range(1, 100, 2):
        print(j)

    5、yield表达式形式(了解)

    def dog(name):
        print("%s准备开吃"%name)
        while True:
            food = yield
            print("%s吃了%s"%(name,food))
    
    g = dog("egon")
    # 初始化生成器,并将“egon”传进去
    g.__next__()
    # 必须先将代码运行至yield才能为其传值
    g.send("狗不理包子")
    # 给yield左边的变量传参,触发了__next__方法
    g.send("饺子")
    
    # 输出
    # egon准备开吃
    # egon吃了狗不理包子
    # egon吃了饺子
    

    注意点:要有一个变量接收yield传的值,传值之前,必须要将代码运行到yield处,传值必须要调用send()方法

    6、yield与return

      ①、yield帮你提供了一种自定义生成器方式

      ②、会帮你将函数的运行状态暂停住

      ③、可以返回值

    与return之间的异同点

      ①、相同点:都可以返回值,并且都可以返回多个

      ②、不同点:yield可以返回多次值,而return只能返回一次值(函数体代码运行到return立即结束)

            yield还可以接受外部传入的值

    四、生成器表达式

    1、之前学了三种生成式:列表生成式、字典生成式、集合生成式,唯独没有元组生成式,那是因为用小括号括起来的式子就不再是一个元组了,而变成了一个生成器。

    list1 = [1, 2, 3, 4, 5]
    d = (i for i in list1 if i != 2)
    print(d)
    # 输出
    # <generator object <genexpr> at 0x031B28A0>
    # 此时的d已经是一个生成器了
    

    2、上述这种形式的式子就是生成器表达式

    3、生成器的特点是省内存,无论在什么时候,内存中只有一块内存空间被占用。

    4、生成器也可以调用__iter__和__next__方法,说明生成器本质上就是一个迭代器。

    5、生成器不会主动执行任何一行代码

    # 一道面试题
    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)
    
    # 问最终的res是什么?
    A. res = [10, 11, 12, 13]
    B. res = [11, 12, 13, 14]
    C. res = [20, 21, 22, 23]
    D. res = [21, 22, 23, 24]
    
    # 分析:首先定义了两个函数,代码运行到g = test()时,由于test函数内部有yield关键字,所以test函数体代码并不会运行,并且g已经初始化为一个生成器,执行到for循环语句,第一次循环,n虽然是等于1传入,但循环内部的代码只是一个赋值语句,实际代码并未执行,此时g = (add(n, i) for i in test()),同理,进行第二次循环的时候,n = 10,g = (add(n, i) for i in (add(n, i) for i in test())),循环结束得到上述两个结果。真正执行代码是运行到list(g)的时候,list()内部是基于for循环将g这个生成器内的值一一取出来,先执行for i in test(),取出[0, 1, 2, 3]四个值,分别交给add函数,由于此时n = 10,所以for i in (add(n, i) for i in test())取出的值为[10, 11, 12, 13],再交给add函数,与n相加,得到[20, 21, 22, 23]
    
    # 分析:n为什么等于10?
    两次for循环,都没有执行代码,所以n的值不会传入表达式,执行完for循环后,n = 10,真正执行代码时,来找n的值,此时n等于10

    五、常用内置方法

    https://www.cnblogs.com/Dominic-Ji/articles/11151528.html#_label2

    # print(abs(-11.11))  # 求绝对值
    # l = [0,1,0]
    # print(all(l))  # 只要有一个为False就返回False
    # print(any(l))  # 只要有一个位True就返回True
    def index():
    
        username = '我是局部名称空间里面的username'
        # print(locals())  # 当前语句在哪个位置 就会返回哪个位置所存储的所有的名字
        print(globals())  # 无论在哪 查看的都是全局名称空间
    # index()
    # print(bin(10))
    # print(oct(10))
    # print(hex(10))
    # print(int('0b1010',2))
    
    # print(bool(1))
    # print(bool(0))
    
    
    # s = 'hello'
    # print(s.encode('utf-8'))
    # print(bytes(s,encoding='utf-8'))
    
    # 可调用的(可以加括号执行相应功能的)
    # l = [1,2,3]
    # def index():
    #     pass
    # print(callable(l))
    # print(callable(index))
    
    
    
    
    
    # print(chr(97))  # 将数字转换成ascii码表对应的字符
    # print(ord('a'))  # 将字符按照ascii表转成对应的数字
    
    """
    面向对象需要学习的方法
    classmethod
    delattr
    getattr
    hasattr
    issubclass
    property
    repr
    setattr
    super
    staticmethod
    """
    # dir获取当前对象名称空间里面的名字
    # l = [1,2,3]
    # print(dir(l))
    #
    # import test
    # print(dir(test))
    # print(test.name)
    
    # divmod  分页器
    
    # print(divmod(101,10))
    # total_num,more = divmod(900,11)
    # if more:
    #     total_num += 1
    # print('总页数:',total_num)
    
    # enumerate 枚举
    # l = ['a','b']
    # for i,j in enumerate(l,1):
    #     print(i,j)
    
    # eval  exec
    s = """
    print('hello baby~')
    x = 1
    y = 2
    print(x + y)
    
    """
    # eval(s)
    # exec(s)
    
    # eval不支持逻辑代码,只支持一些简单的python代码
    s1 = """
    print(1 + 2)
    for i in range(10):
        print(i)
    """
    # eval(s1)
    # exec(s1)
    
    
    # name = 'jason'
    # s2 = """
    # name
    # """
    # print(eval(s2))
    
    # format 三种玩法
    # {}占位
    # {index} 索引
    # {name} 指名道姓
    
    # print(globals())
    def login():
        """
        一起嗨皮
        :return:
        """
    # print(help(login))
    
    # isinstance 后面统一改方法判断对象是否属于某个数据类型
    # n = 1
    # print(type(n))
    # print(isinstance(n,list))  # 判断对象是否属于某个数据类型
    
    # print(pow(2,3))
    
    
    # print(round(3.4))

    六、面向过程编程

    1、就类似于设计一条流水线

    2、好处:将复杂的问题流程化,从而简单化

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

    # 注册功能
    # 1.获取用户输入
    def get_info():
        while True:
            username = input(">>>:").strip()
            if not username.isalpha():  # 判断字符串不能包含数字
                print('不能包含数字')
                continue
            password = input('>>>:').strip()
            confirm_password = input("confirm>>>:").strip()
            if password == confirm_password:
                d = {
                    '1':'user',
                    '2':'admin'
                }
                while True:
                    print("""
                        1 普通用户
                        2 管理员
                    """)
                    choice = input('please choice user type to register>>>:').strip()
                    if choice not in d:continue
                    user_type = d.get(choice)
                    operate_data(username,password,user_type)
                    break
            else:
                print('两次密码不一致')
    
    # 2.处理用户信息
    def operate_data(username,password,user_type):
        # jason|123
        res = '%s|%s|%s
    '%(username,password,user_type)
        save_data(res,'userinfo.txt')
    
    # 3.存储到文件中
    def save_data(res,file_name):
        with open(file_name,'a',encoding='utf-8') as f:
            f.write(res)
    
    def register():
        get_info()
    
    register()

    七、补充

    1、读取一个文件并返回每行数据的长度

    with open('test1.txt', 'w', encoding='utf-8') as f:
        for line in range(1000):
            f.write(f'www{line}aaa' * (line + 1) + '
    ')
    
    # 列表推导式: 处理数据量大的文件会导致内存溢出.
    res = [len(line) for line in open('test1.txt', 'r', encoding='utf-8')]
    # print(res)
    
    # 生成器表达式: 处理数据量大的文件推荐使用.
    res2 = (len(line) for line in open('test1.txt', 'r', encoding='utf-8'))
    print(res2)  # <generator object <genexpr> at 0x000002B3748FD0A0>
    print(next(res2))
    print(next(res2))
    

    2、一个面试题

    # 题目
    def multipliers():
        return [lambda x: i*x for i in range(4)]
    print([m(2) for m in multipliers()])
    # 输出[6, 6, 6, 6]
    
    
    # 改写
    def multipliers2():
        list1 = []
        for i in range(4):
            def func(x)
                return x * i
            list1.append(func)
        return list1
    print([m(2) for m in multipliers2()])
    
    # 原因:闭包函数的延迟绑定
    # 在内层函数执行时才会绑定变量i
    
    #如果
    def multipliers():
        return [lambda x,i =i: i*x for i in range(4)]
    print([m(2) for m in multipliers()])
    # 输出[0, 2, 4, 6]
    
    # 改写
    def multipliers2():
        list1 = []
        for i in range(4):
            def func(x,i=i)
                return x * i
            list1.append(func)
        return list1
    print([m(2) for m in multipliers2()])
    
    # 由于在内层函数定义阶段,i是由参数传入的值,那么func函数就能直接拿到i的值。每一次for循环i的值都不一样,所以每一次func函数的定义体代码都不一样。
    

    3、三个老母鸡的故事

       g,g1,g2里面的值都是指向同一个内存地址,被一个老母鸡取走了,其他母鸡就没有了。

    def demo():
        for i in range(4):
            yield i
    g = demo()
    g1 = (i for i in g)
    g2 = (i for i in list(g1))
    
    print(list(g))
    print(list(g1))
    print(list(g2))
    # 输出
    []
    []
    [0, 1, 2, 3]
  • 相关阅读:
    C#程序调试
    jsp连接sql数据库
    SQL记录
    对于和/的小问题:证明路径中可以混合使用斜杠和反斜杠
    集合初识
    details.jsp页面的 response.addCookie(cookie);报错&tomcat高版本下的Cookie问题
    sql查询操作—顺序查询
    myeclipse使用Microsoft JDBC Driver 6.0 for SQL Server连接sql
    JavaScript、Java、C#关于for循环的比较
    关于jsp动作元素的一点疑惑
  • 原文地址:https://www.cnblogs.com/DcentMan/p/11191594.html
Copyright © 2020-2023  润新知