• python迭代器-迭代器取值-for循环-生成器-yield-生成器表达式-常用内置方法-面向过程编程-05


    迭代器

    迭代器

      迭代: # 更新换代(其实也是重复)的过程,每一次的迭代都必须基于上一次的结果(上一次与这一次之间必须是有关系的) 

      迭代器: # 迭代取值的工具 

    为什么用迭代器: # 迭代器提供了一种可以不依赖索引取值的方式 

    # 会一直打印0,记得停止
    # n = 0
    # while True:
    #     print(n)
    
    # 重复 + 每次迭代都是基于上一次的结果而来的
    l = [1, 2, 3, 4, 5]
    s = 'hello'
    n = 0
    while n < len(s):
        print(s[n])
        n += 1
    # h
    # e
    # l
    # l
    # o
    迭代小案例

    可迭代对象

    '''
    可迭代对象
        只要内置有 __iter__ 方法的都叫做可迭代对象(读:双下iter)
            补充: 针对双下划线开头结尾的方法,推荐读“双下 + 方法名”
        基本数据类型中,是可迭代对象的有(str, list, tuple, dict, set), 文件对象(执行 __iter__ 之后还是本身,__next__之后还是他本身)
    
        可迭代对象执行内置的 __iter__ 方法,得到的就是该对象的迭代器对象
    '''
    # 验证哪些类型是可迭代对象类型
    
    n = 1
    f = 1.1
    s = 'hello'
    l = [1,2,34,]
    t = (1,2,34)
    s1 = {1,2,3,4}
    d = {'name':'jason'}
    f1 = open('xxx.txt','w',encoding='utf-8')
    
    # int、float 类型没有 __iter__() 方法,他们不是可迭代对象
    # print(type(n.__iter__()))  # 会直接报错,AttributeError: 'int' object has no attribute '__iter__'
    # print(type(f.__iter__()))  # 会直接报错,AttributeError: 'float' object has no attribute '__iter__'
    
    print(s.__len__())  # 简化成了len(s)
    # 5
    
    # str、list、tuple、set、dict、文件对象 都有 __iter__() 方法,都是可迭代对象
    print(type(s.__iter__()))  # 同:res = iter(s)
    print(type(l.__iter__()))
    print(type(t.__iter__()))
    print(type(s1.__iter__()))
    # <class 'str_iterator'>
    # <class 'list_iterator'>
    # <class 'tuple_iterator'>
    # <class 'set_iterator'>
    print(type(d.__iter__()))
    # <class 'dict_keyiterator'>
    
    print(type(f1.__iter__()))
    # <class '_io.TextIOWrapper'>  # 还是文件对象本身
    
    # 可迭代对象执行内置的__iter__方法得到就是该对象的迭代器对象

    迭代器对象与可迭代对象的区别

    '''
    迭代器对象和可迭代对象有什么共同点
        迭代器对象
            1.内置有双下iter( __iter__) 方法
            2.内置有 __next__ 方法
            ps:迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象
    '''

    __iter__ 转换迭代器对象

    l = [1, 2, 3, 4]
    # 生成一个迭代器对象
    iter_l = l.__iter__()
    
    # 迭代器取值 调用__next__
    print(iter_l.__next__())
    print(iter_l.__next__())
    print(iter_l.__next__())
    print(iter_l.__next__())
    # 1
    # 2
    # 3
    # 4
    # print(iter_l.__next__())  # 取完了 直接报错
    
    
    d = {'name':'jason','password':'123','hobby':'泡m'}
    # 将可迭代对象d转换成迭代器对象
    iter_d = d.__iter__()
    # 迭代器对象的取值 必须用__next__
    print(iter_d.__next__())
    print(iter_d.__next__())
    print(iter_d.__next__())
    # name
    # password
    # hobby
    # print(iter_d.__next__())  # 取完了 报错StopIteration
    
    f1 = open('test1.txt', 'r', encoding='utf-8')
    # 调用f1内置的__iter__方法
    iter_f = f1.__iter__()
    print(iter_f is f1)
    # True
    
    """
    迭代器对象无论执行多少次__iter__方法得到的还是迭代器对象本身(******)
    """
    print(iter_l is iter_l.__iter__())
    # True
    print(iter_l is iter_l.__iter__().__iter__().__iter__().__iter__().__iter__())
    # True
    
    print(iter_d is iter_d.__iter__())
    # True
    print(iter_d is iter_d.__iter__().__iter__().__iter__().__iter__().__iter__())
    # True
    
    print(f1 is f1.__iter__().__iter__().__iter__().__iter__())
    # True
    迭代器迭代取值与多次迭代示例

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

    疑问: # __iter__方法就是用来帮我们生成迭代器对象,而文件对象本身就是迭代器对象,为什么还内置有__iter__方法??? ---见后面的for循环内部原理 

    next 取值越界错误处理

    d = {'name': 'jason', 'password': '123', 'hobby': '泡m'}
    iter_d = d.__iter__()  # 转换为迭代器对象
    
    # 比起一次次调用 next取值,我们肯定选择用循环来取值呀,那如何解决取值越界的问题呢?
    while True:
        try:
            print(iter_d.__next__())
        except StopIteration:  # 取值越界了会报错,那么我们用 except 关键字捕获它,并结束循环,代表取值结束了
            print('老母猪生不动了')
            break
    # name
    # password
    # hobby
    # 老母猪生不动了

    可迭代对象与迭代器对象小结

    '''
    可迭代对象:内置有 __iter__ 方法
    迭代器对象:既内置有__iter__ 方法,也内置有__next__方法
    
    迭代取值的优点与缺点:
        优点:
            不依赖于索引取值
            内存中永远只占一份空间,不会导致内存溢出
        缺点:
            不能获取指定的元素
            取完之后会报StopIteration错..(可手动捕获并处理)
    '''

    for循环内部原理

    '''
    for 循环内部的本质
        1.首先让 in 关键字后面的对象调用 __iter__ 转换成迭代器对象   --> 所以前面的疑问,文件对象本来就是迭代器对象还有 __iter__ 方法,是为了扩展它可以支持 for 循环
        2.调用__next__ 迭代取值
        3.内部有异常捕获,StopIteration,当__next__报这个错,自动结束循环
    '''

     内部原理同上面 next 取值越界错误处理 里的案例

    生成器

    生成器: # 用户自定义的迭代器,生成器本质就是迭代器 

    注意点: # 函数内如果有yield关键字,那么加括号执行函数的时候并不会触发函数体代码的运行 , # yield后面跟的值就是调用迭代器__next__方法你能得到的值 , # yield既可以返回一个值也可以返回多个值 并且多个值也是按照元组的形式返回 

     yield 返回值(体现上面的注意点)

    def func():
        print('first')
        yield  # 函数内如果有yield 关键字,那么加括号执行函数的时候并不会触发函数体代码的运行  ***
        print('second')
        yield 2  # yield 后面跟的值就是调用迭代器 __next__ 方法你能得到的值
        print('third')
        yield 3, 4, 5  # 既可以返回一个值,也可以返回多个值,并且多个值也是按照元组的形式返回
    
    
    g = func()  # 生成器初始化:将函数变成迭代器
    # g.__iter__()
    print(g.__next__())
    # first
    # None
    
    print(g.__next__())
    # second
    # 2
    
    print(g.__next__())
    # third
    # (3, 4, 5)
    利用yield返回值

    小案例

    # 自定义range 功能
    for i in range(10):
        print(i, end='- ')
    print()
    
    
    def my_range(start, end, step=1):  # 只传一个参数的,range,判断参数个数  搜:python只传一个参数的range
        while start < end:
            yield start
            start += step
    
    
    for i in my_range(1, 10, 3):
        print(i)
    # 1
    # 4
    # 7
    自定义实现range

    yield表达式形式传入参数

    # yield 表达式形式(传参的第三种方式,直接传参,闭包)
    def dog(name):
        print("%s 准备开始吃" %name)
        while True:
            food = yield
            print("%s 吃了 %s" %(name, food))
    
    
    print(dog)
    # <function dog at 0x000001C22A10BA60>
    
    # 当函数内有yield 关键字的时候 调用该函数 不会执行函数体代码
    #   而是将函数变成生成器
    g = dog("egon")  # 把函数变成生成器,函数体没有运行
    g.__next__()  # 不运行这个直接运行下面的会直接报错 TypeError: can't send non-None value to a just-started generator
    # 必须将代码运行至 yield 才能为其传值
    # egon 准备开始吃
    g.send('狗不理包子')  # 给yield 左边的变量传参,触发了 __next__ 方法
    # egon 吃了 狗不理包子
    g.__next__()
    # egon 吃了 None
    g.send('饺子')
    # egon 吃了 饺子
    利用yield传入参数

    总结yield并与return作对比

    '''
    总结yield:
        1.yield 提供了一种自定义生成器的方式
        2.yield 会帮你将函数的运行状态暂停住
        3.yield 可以返回值
        
    与return 之间的异同点:
        相同点:
            1.都可以返回值,并且都可以返回多个,返回多个都是包装成元组
        不同点:
            1.yield 可以返回多次值,而return  只能返回一次函数立即结束
            2.yield还可以接收外部传入的值
        
    '''
    总结与对比

    常用内置方法

    常见数学内置函数 abs 绝对值      pow 求平方    round  四舍五入   divmod 将运算结果除数与余数存入元组

    # abs 求绝对值
    print(abs(-10.11))
    # 10.11
    
    # pow 平方
    print(pow(2, 4))
    # 16
    
    # round 四舍五入
    print(round(3.1656))
    # 3
    
    # divmod 将运算结果除数与余数存入元组(可用于分页器)
    print(divmod(100, 11))
    # (9, 1)
    all_count = 101
    counts_each_page = 5
    total_pages, more = divmod(all_count, counts_each_page)
    if more:
        total_pages += more
    print(total_pages)
    # 21  --> 共101条内容,按每页5条来分,需要分21页
    abs pow round divmod

    数据类型进制转换  isinstance(判断数据类型)  all(传过来容器类型必须全部都是True才返回True)   any(有一个是True就返回True)   bool(将传进来参数变成布尔类型)   bin(十转二) oct(十转八)  hex(十转十六)    int(把指定类型转成十进制)

    # isinstance 判断数据类型,后面统一用该方法判断对象是否属于某一个数据类型
    n = 1
    print(type(n))
    print(isinstance(n, list))  # 判断对象是否属于某个数据类型
    # <class 'int'>
    # False
    
    # all 传过来的容器类型里必须全部是True,否则返回False
    # any 只要一个True,就返回True
    l = [0, 1, 2]
    print(all(l))  # 只要有一个是False,就返回False
    print(any(l))
    # False
    # True
    
    # bool
    print(bool(1))
    print(bool(0))
    # True
    # False
    
    
    # bin  oct  hex 将十进制转换成对应的进制
    print(bin(10))
    print(oct(10))
    print(hex(10))
    # 0b1010 二进制
    # 0o12 八进制
    # 0xa 十六进制
    
    # int 可以把指定进制转成十进制
    print(int('0b1010', 2))
    # 10
    isinstance all any bool bin oct hex int

    字符编码  ascii (个人觉得没啥用)  chr(将数字转换成ascii码 对应的字符)    ord (将字符按照ascii码 转成数字)  bytes(转成二进制)

    # ascii  个人觉得没啥用
    print(ascii('a'))
    # 'a'
    print(ascii(65))
    # 65
    print(ascii('c'))
    # 'c'
    
    # chr  ord 将数字转换成ascii 码表对应的字符  将字符按照ascii 码表转成对应的数字
    print(chr(65))
    # A
    print(ord('A'))
    # 65
    
    # bytes 转换成二进制
    # s = b'hello'
    # print(s)
    # # b'hello'
    s = 'hello'
    print(s.encode('utf-8'))
    print(bytes(s, encoding='utf-8'))
    # b'hello'
    # b'hello'
    ascii chr ord bytes

    可迭代对象常见内置函数  slice  切片(生成的是老母猪,节省空间)  enumerate  给可迭代对象生成索引(假索引)

    # slice 切片
    myslice = slice(5)    # 设置截取5个元素的切片
    arr = range(10)
    print(arr)
    print(arr[myslice])  # 截取5个元素
    print(arr)
    # range(0, 10)
    # range(0, 5)
    # range(0, 10)
    
    
    # enumerate 枚举 可以指定起始位
    l = ['a', 'b', 'c']
    for i, j in enumerate(l, 1):
        print(i, j)
    # 1 a
    # 2 b
    # 3 c
    slice enumerate

    执行字符串中的python语句以及python字符串格式化  eval (执行字符串中的python语句,不够强大)   exec (执行python中的语句,能执行逻辑的)   format(格式化字符串)

    # eval  exec 执行字符串里的代码,eval 不支持逻辑代码,只支持一些简单的python代码, exec可以执行逻辑代码
    s = """
    print('hello baby~')
    # x = 1
    # y = 2
    # print(x + y)
    """
    
    # eval(s)
    # hello baby~
    exec(s)
    # hello baby~
    
    # format 三种玩法
    # ------ {} 占位 --------
    print('{} like {}'.format('tank', 'jason'))
    # tank like jason
    # ------ {index} 索引 --------
    print('{0} like {2}'.format('tank', 'egon', 'jason'))
    # tank like jason
    # ------ {name} 指名道姓(关键字) --------
    print('{name} is {age} years old.'.format(age=18, name='jason'))
    # jason is 18 years old.
    eval exec format

    对象命名空间 callable(对象是否可调用)  dir (获取该对象名称空间里的名字)  globals (查看全局名称空间的名字)    locals (返回当前位置名称空间的名字)  help (查看函数的注释)

    # callable 可调用的,可以加括号执行相应功能
    def index():
        pass
    l = [1, 2, 3, 4, 5]
    print(callable(index))
    print(callable(l))
    # True
    # False
    
    # dir 获取当前对象名称空间里面的名字(所有他能够支持的名字),可以是任何对象
    l = [1, 2, 3]
    print(dir(l))
    # ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
    # import test
    # print(dir(test))
    # print(test.name)
    
    
    # globals 无论在哪,查看的都是全局名称空间
    # locals 当前语句在那个位置,就会返回哪个位置所存储的所有名字
    def index():
        '''
        index的注释
        :return: 没有返回值
        '''
        username = "局部名称空间里的username"
        print(locals())  # 当前语句在那个位置,就会返回哪个位置所存储的所有名字
    
    
    print(globals())  # 不受位置影响
    # {'__name__': '__main__'-------省略一大堆------- 'l': ['a', 'b'], 'i': 2, 'j': 'b'.....>}
    index()
    # {'username': '局部名称空间里的username'}
    
    
    # help 查看函数注释
    print(help(index))
    # Help on function index in module __main__:
    #
    # index()
    #     index的注释
    #     :return: 没有返回值
    #
    # None
    callable dir globals locals help

    面向过程编程

    概述: # 面向过程就类似于流水线设置,将复杂问题分步实现 

    优点: # 可以将复杂的问题流程化,从而简单化 

    缺点: # 可扩展性差,如有改动,可能会涉及超大面积更改 

    小案例(牵一发动全身)

    # 获取用户登录
    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:
                operate_data(username,password)
                break
            else:
                print('两次密码不一致')
    
    # 2.处理用户信息
    def operate_data(username,password):
        # jason|123
        res = '%s|%s
    '%(username,password)
        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.获取用户输入
    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.请判断下面代码中的 res 是什么

    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]
    # C. res = [20, 21, 22, 23]
    答案

    2.请判断下面程序会输出什么

    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))
    # 已经提前把里面东西取出来了
    
    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]
    答案

    3.利用生成器表达式读取文件行数

    '''
    需求: 读取一个文件并返回每行数据的长度
    '''
    # 创建一个测试文件,往里面输入内容
    with open('test1.txt', 'w', encoding='utf-8') as f:
        for line in range(1000):
            f.write(f'www{line}aaa' * (line + 1) + '
    ')
    
    # 内容示例
    '''
    www0aaa
    www1aaawww1aaa
    www2aaawww2aaawww2aaa
    www3aaawww3aaawww3aaawww3aaa
    www4aaawww4aaawww4aaawww4aaawww4aaa
    www5aaawww5aaawww5aaawww5aaawww5aaawww5aaa
    ......省略.....
    ...
    '''
    # 列表推导式: 处理数据量大的文件会导致内存溢出.
    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))
    解决方案

    4.写出下面代码的执行结果

    # 请写出下面代码的执行结果
    def mutipliers():
        return [lambda x: i*x for i in range(4)]
    
    
    print([m(2) for m in mutipliers()])
    # [6,6,6,6]  ---> 题目的正确答案
    def multipliers():
    
        return [lambda x, i=i: i*x for i in range(4)]
        # 0, 1, 2, 3
        # [func(x): return 0*x, func(x): return 1*x,
        # func(x): return 2*x, func(x): return 3*x, ]
    
    print([m(2) for m in multipliers()])  # [0, 2, 4, 6]
    
    # [func(x): return 0*2, func(x): return 1*2,
    # func(x): return 2*2, func(x): return 3*2, ]
    # [0, 2, 4, 6]
    # [6, 6, 6, 6]
    
    # 闭包函数的延迟绑定
    # 在内层函数执行时才会绑定变量i
    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()])  # [0, 2, 4, 6]
    
    # [0, 2, 4, 6]
    思路(根据思路题目更改答案变更)
  • 相关阅读:
    方差、协方差、相关系数的理解
    yii2原生sql
    Oracle中日期作为条件的查询
    IDEA 中tomcat图片储存和访问虚拟路径(图片和程序分家)
    nginx配置静态资源:配置绝对路径
    一般spring配置上下文
    spring boot 集成 redis lettuce(jedis)
    windows下面同时部署多个tomcat的方法
    oracel: 通过特殊表序列来实现oracle自增id (mybatis实现自增id)
    使用fastjson 进行jsonObject转实体类对象
  • 原文地址:https://www.cnblogs.com/suwanbin/p/11189321.html
Copyright © 2020-2023  润新知