迭代器

迭代器

1.迭代器及相关概念
    迭代器:是用来迭代取值的工具
    迭代:是一个重复的过程,但每次的重复都是基于上一次的结果进行的,单纯的重复不叫迭代,如例1,类似于例2才是迭代的过程
    # 例1
    while True:
        print(1+1)
    # 例2
    count = 1
    while True:
        print(count)
        count += 1
    可迭代对象:内置有__iter__方法的对象都是可迭代对象,例如字符串、列表、元组、字典、集合以及文件,都有内置__iter__方法,都为可迭代对象,整型和浮点型不是可迭代对象
    'richer'.__iter__()
    [1, 2, 3].__iter__()
    (1, 2, 3).__iter__()
    {'x':1, 'y':2}.__iter__()  
    {1, 2, 3}.__iter__()
    open('a.txt',mode='r').__iter__()
    迭代器对象:调用可迭代对象的__iter__()会得到一个返回值,该返回值便是迭代器对象
        特点:内置有__iter__()和__next__()方法,例如文件,既是可迭代对象,又是迭代器对象
        ps:可迭代对象不一定是迭代器对象,但迭代器对象一定是可迭代对象
    open('a.txt',mode='r').__iter__()
    open('a.txt',mode='r').__next__()
2.为什么要用迭代器
    names = ['richer', 'lili', 'jack', 'kiki', 'tom']
    # 需求,将列表中的元素逐个取出,不用for循环的情况下
    i = 0
    while i < len(names):
        print(names[i])
        i += 1
    # 但while True迭代取值只适用于索引类型取值,不适用于字典、集合等非索引类型
    因此使用迭代器取值
    优点:
        1)是一种通用的迭代取值的方案 -> 同for循环,又称迭代循环
        for 循环的工作原理
            names = ['richer', 'lili', 'jack', 'kiki', 'tom']
            # names = {"x":1,"y":2,'z':3}  # 字典迭代取的值是key
            # names.__len__()可以写成len(names),同理names.__iter__() -> iter(names)
            iter_names = iter(names) # 可迭代对象转换未迭代器对象
            while True:
            # 单纯while True取完值后会报错,避免没有匹配到异常,又中断程序,用try/except
                try:  #  捕获程序在执行时遇到的异常情况,即一些报错。
                    print(next(iter_names))  # iter_names.__next__()->next(iter_names)
                except StopIteration:  # 发现StopIteration错误时继续向下执行
                    break
            i.调用in后的对象__iter__方法,得到一个迭代对象的iter_names
            ii.next(iter_names)赋值打印,然后运行循环体代码
            iii.重复步骤2,直到取完值,抛出异常,for循环会帮我们捕捉异常结束循环
        2)惰性计算,节省内存,例如python3种的range
    缺点:
        1)不能取指定的值,只能往后next
            s = "abc"
            iter_s = s.__iter__()  # iter_s = iter(s) # 可迭代对象转换未迭代器对象
            print(iter_s.__next__())  # print(next(ites_s))  a
            print(iter_s.__next__())  # print(next(ites_s))  b
            print(iter_s.__next__())  # print(next(ites_s))  c
            print(iter_s.__next__())  # 报错  StopIteration
           

            dic = {"x":1,"y":2}
            iter_dic = ter(dic) # 可迭代对象转换未迭代器对象
            print(next(iter_dic))  # x
            print(next(iter_dic))  # y
        2)迭代器对象是一次性的:一个迭代器对象值去干净后,不能继续取,继续取,报错如1或不出值(for循环)
            names = ['richer', 'lili', 'jack', 'kiki', 'tom']
            iter_names = iter(names) # 可迭代对象转换未迭代器对象
            for x in iter_names :
                print(x)

            print('='*50)

            for x in iter_names :
                print(x)
            输出结果:
            	egon
			   tom
		  	   jack
			   lili
			   ttt
		        ==================================================
    3、如何用迭代器
        1)使用__iter__()或iter(),将可迭代对象转换成迭代器对象
        2)使用__next__()或next(),运行代码

生成器

生成器:自定义迭代器
l=[1,2,3]
res=iter(l)
print(next(res)) # 1
print(res)  # <list_iterator object at 0x000001BA60D46BB0>  返回iterator迭代器内存地址 iterable 可迭代对象

在函数中迭代取值需要返回值时
def func():
    print('one')
    return 1
    print('two')
    return 2
    print('three')
    return 3
res = func()
print(res)  # one  1    return只运行一次就结束了函数
为了返回多次值,用到yeild
def func():
    print('one')
    yeild 1
    print('two')
    yeild 2
    print('three')
    yeild 3
    print('four')
函数内但凡出现yeild关键字,再调用函数不会立即执行函数代码体,而是会返回一个生成器对象,生成器对象本质就是一个自定义的迭代器对象
调用函数,得到返回操作如下:
g = func()
# g.__iter__()  
# g.__next__()
print(g)  # one <generator object func at 0x000002145B28D820> generator生成器

res = next(g)
print(res)  # one  1

res = next(g)
print(res)  # two  2

res = next(g)
print(res)  # three  3

next(g)  # four 报错 StopIteration,会继续执行代码,但无返回值

函数递归

1.什么是函数递归
    函数递归是函数嵌套调用的一种特殊格式,即再调用一个函数时,在其内部又调用了自己,即循环
    def foo():
        print('from foo')
        foo()
   
    # 循环调用是有层数限制的,限制为1000层,可以修改,但不建议
    # import sys

    # print(sys.getrecursionlimit())  # 1000
    # sys.setrecursionlimit(2000)
    foo()  
    
    递归分为两个阶段
        1)回溯:溯源,寻根问底,一层一层往下调
        2)递推:得到源头后,一层一层往上推出结果
   
2.为何要用函数递归
    为循环机制提供了一种基于函数实现的新的方式

3.如何用函数递归
    # 用函数得出age(5)的结果
    # age(5)=age(4)+10
    # age(4)=age(3)+10
    # age(3)=age(2)+10
    # age(2)=age(1)+10
    # age(1)=18
    
    # 进一步思考
    # n > 1: age(n) = age(n-1)
    # n == 1: age(1) = 18
    
    # 函数实现
    def age(n):
        if n > 1:
            return age(n-1) + 10
        # else:
             # return 18
        return 18 #  两行成一行
    
    res = age(5)
    print(res)  # 58
    
    # 回溯
    # 第一次:n=5
    # age(5) = age(4) + 10

    # 第二次:n=4
    # age(4) = age(3) + 10

    # 第三次:n=3
    # age(3) = age(2) + 10

    # 第四次:n=2
    # age(2) = age(1) + 10

    # 第五次:n=1
    # age(1) = 18
    
    # 例1
    # 需求:取出非列表和元组的值
    l = [1,[2,[3,(4,(5,[6,[7,(8,(9,[10]))]]))]]]

    def func(l):
        for i in l:
            if type(i) is list:
                func(i)
            elif type(i) is tuple:
                func(i)
            else:
                print(i)

    func(l)
    
    # 例2:递归实现二分法
    nums = [3, 7, 9, 13, 21, 33, 57, 63, 98]
    
    def catch(nums, find_num):
        print(nums)
        if len(nums) == 0:  # 要找的值没在列表中,则二分到最后为空列表
            print('Blank')
            return
        mid = len(nums) // 2  # // 二分
        if find_num > nums[mid]:
            # 找右半部分
            catch(nums[mid+1:], find_num)  
        elif find_num < nums[mid]:
            # 找左半部分
            catch(nums[:mid],find_num)  # [:mid] 顾头不顾尾
        else:
            print('find it')
            
    catch(nums, 33)  
    输出结果:
        [3, 7, 9, 13, 21, 33, 57, 63, 78, 99]
        find it
        
    catch(nums, 35)
        [3, 7, 9, 13, 21, 33, 57, 63, 78, 99]
        [57, 63, 78, 99]
        [57, 63]
        [57]
        []
        not exists

三元表达式

# 可以将多条代码写成一条,简化代码,也不丧失可读性,但只能是if else这种双分支结构或两个条件的简单代码,复杂的不建议写成多元表达式
书写方式
    变量名 = 条件成立时的返回值 if 条件 else 条件不成立时的返回值
    res = 'ok' if 'a' != 'b' else 'no'
# 例子:将下列代码写成三元表达式
x = 10
y = 20
if x > y:
    return x
else:
    return y
# 实现
res = x if x > y else y
print(res)

生成式

1.列表生成式:映射新列表,根据条件筛选出新列表
l = []
# for i in range(10):
#     if i > 3:
#         l.append(i)
l = [i for i in range(10) if i > 3]
print(l)   # [4, 5, 6, 7, 8, 9]

names = ['Zhangsan', 'Lisi', 'Wangmazi', 'Zhangquandan', 'Zhangdapao']
# 需求:将姓Zhang的提取出来
res = [name for name in names if name.startswith('Zhang')]
print(res)  # ['Zhangsan', 'Zhangquandan', 'Zhangdapao']
# 需求:将names 这个列表中的元素都加上'_NB'
res = [name + '_NB' for name in names]  
print(res)  
输出结果:
	['Zhangsan_NB', 'Lisi_NB', 'Wangmazi_NB', 'Zhangquandan_NB', 'Zhangdapao_NB']
    
2.字典生成式
dic = {f'{i}': i for i in range(3)}
print(dic)  # {'0': 0, '1': 1, '2': 2}

3.集合生成式
set = {i for i in range(3)}
print(set)  # {0, 1, 2}

4.生成器表达式
res = (i for i in range(3))
print(res) #<generator object <genexpr> at 0x000001FBA78ED820> 输出的是生成器,而非元组
print(next(res))  # 0
print(next(res))  # 1
print(next(res))  # 2

# 若求文件中字符总数
with open('a.txt', mode='rt', encoding='utf8') as f:
    res = f.read()  # 如果文件小,可以用这种方法一次性读出来
    print(len(res))
    
    # 如果文件较大,不适宜一次性读出,应当一行一行读出再求和
    res = 0
    for line in f:
        res += len(line)  # 每一行字符累加
        
    将上面三行代码一行搞定
    # res = sum([len(line) for line in f]) 
    # 如果文件很大,有多行,该法也就不合适了,因此将中括号改为小括号,将它做成生成器
    # res = sum((len(line) for line in f))  # python  语法sum可以将(len(line) for line in f)默认为生成器,因此可以去掉一层括号
    res = sum(len(line) for line in f)
posted @ 2021-07-02 19:31  Richer-J  阅读(88)  评论(0)    收藏  举报