迭代器和生成器

1.迭代器

什么是迭代器

  迭代:更新换代的过程,每次迭代都需要基于上次的结果,是Python最强大的功能之一,是访问集合元素的一种方式

  迭代器,更新换代取值的工具

  迭代器从第一个元素开始访问,直到最后一个元素访问完毕,只能往前

为什么使用迭代器

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

每次迭代都是基于上一次的结果而来的

一个简单的迭代器

# 每次迭代都是基于上一次的结果
l =[1,2,3,4]
i = 0
while i < len(l):
    print(l[i])
    i += 1

可迭代的数据类型

  字符串、列表、元组、字典、集合

 

2.可迭代对象

内置了__iter__方法的都可以叫做可迭代对象

  __iter__读做双下iter

基本数据类型中的可迭代对象

  字符串、列表、元组、字典、集合

 1 # 可以跟__iter__的基本数据类型
 2 s = 'sxc'
 3 l = [1,2,3,4]
 4 t = (1,2,3,4)
 5 d = {'name':'sxc'}
 6 s1 = {1,2,3,4}
 7 s.__iter__()
 8 l.__iter__()
 9 t.__iter__()
10 d.__iter__()
11 s1.__iter__()

这些基本数据类型可以加__iter__,并且并未报错

 

文件类型

1 f =open('aaa.txt','w',encoding='utf-8')
2 f.__iter__()

 

文件类型也是可迭代对象

 

3.迭代器对象

迭代器对象

  内置有__iter__方法并且内置有__next__方法的可以叫做迭代器对象

  所以,迭代器对象一定是可迭代对象,但是可迭代对象,不一定是迭代器对象

  基本数据类型(可迭代对象)后面跟__iter__之后产生的就是迭代器对象

一个迭代器

1 s = 'sxc'  # 字符串s是可迭代对象
2 iter_1 = s.__iter__()  # 后面跟了__iter__变成了迭代器对象
3 print(iter_1)  # 这就是一个迭代器,相当于老母猪
4 print(iter_1.__next__())  # 打印之后就能取值,每次只能取一个
5 print(iter_1.__next__())
6 print(iter_1.__next__())
7 # print(iter_1.__next__())  # 打印的值不能超过原本的范围,会报错StopIteration

需要注意的是,一旦超过取值范围就会报错:StopIteration

 

迭代器对象的__iter__方法

1 f =open('aaa.txt','r',encoding='utf-8')
2 print(f)  # 是可迭代对象也是迭代器对象
3 iter_f = f.__iter__()  # 调用__iter__方法
4 print(iter_f)  # 打印出来的还是本身
5 iter_f1 = iter_f.__iter__()
6 print(iter_f1)  # 还是本身
7 print(iter_f1.__next__())
8 print(iter_f1.__next__())
9 print(iter_f1.__next__())

 

注意:迭代器对象不管调用多少次调用了__iter__方法之后还是本身

一个小问题

问:__iter__方法就是用来帮我们生成迭代器对象而文件对象本身就是迭代器对象,为什么还内置有__iter__方法???

答:因为python中的迭代场景必须使用可迭代对象,而可迭代对象必须有__iter__方法,并且有了__iter__方法之后可以兼容python已有的迭代场景和工具,例如:for循环、列表解析、map、sum、zip等等。

 

异常处理

 一个迭代取值的过程

1 l = [1,2,3,4]
2 iter_l = l.__iter__()
3 print(iter_l.__next__())  # 运行第一次得到1
4 print(iter_l.__next__())  # 运行第二次得到2
5 print(iter_l.__next__())  # 运行第三次得到3
6 print(iter_l.__next__())  # 运行第四次得到4
7 print(iter_l.__next__())  # 运行第五次列表内的值已取完,报错StopIteration

我们可以进行异常处理消除掉报错

l = [1,2,3,4]
iter_l = l.__iter__()
while True:  # 循环执行
    try:
        print(iter_l.__next__())
    except StopIteration:  # 如果报错StopIteration,就返回下面这句话,从而消除报错
        print('列表中的值已取完')
        break

注意:迭代取值只能一个个往后取值,不能往前取值

 

4.for循环的本质

for循环内部的本质:

  1.将in后面的可迭代对象循环调用__iter__方法转化为迭代器对象

  2.调用__next__方法迭代循环取值

  3.当内部异常报错StopIteration时,自动结束循环

其实和上面的异常处理类似,迭代取值,只不过当发现异常之后直接结束,不返回或者打印任何值

1 l = [1,2,3,4]
2 for i in l:  # 这两行代码通过内置方法,完成了下面的代码
3     print(i)
4 # iter_l = l.__iter__()
5 # while True:  # 循环执行
6 #     try:
7 #         print(iter_l.__next__())
8 #     except StopIteration:  # 如果报错StopIteration,就返回下面这句话
9 #         break

 

迭代取值的优缺点:
  优点:1.不依赖索引取值

     2.每次取值都只占一小份空间,节省空间

  缺点:1.不能指定取某个值,只能往前,不能往后

     2.值取完之后会报错StopIteration

 

一些内置函数也是基于for循环使用迭代器的方法

1 l1 = [1,2,3,4]
2 l2= [4,3,2,1]
3 res = map(lambda x,y:x+y,l1,l2)
4 print(res)  # 结果表明了他是一个迭代器
5 print(res.__next__())
6 print(res.__next__())
7 print(res.__next__())
8 print(res.__next__())
9 print(list(res))  # 每次迭代取值之后,res的值都是被取走的,不在存于res中

内置函数map()、zip()等都是使用迭代器的方法,因为这样能节省内存

 

5.生成器

什么是生成器

  用户自己定义的迭代器,本质就是迭代器,他的特点是使用了yiled

  用户在调用生成器每次运行的时候,遇到yiled就暂停运行并且返回后面跟的值,直到下一次执行__next__方法时再从当前位置继续执行

 1 def func():
 2     print('第一次')
 3     yield 111  # 函数内部如果有yield,则遇到func()不会执行函数体
 4     print('第二次')
 5     yield 222
 6     print('第三次')
 7     yield 333,444,555,666
 8 # 因为函数内部又yield,所以函数体代码并不会执行
 9 g = func()  # 生成器初始化:将函数变为迭代器
10 # 调用__next__方法之后,函数体才会执行,并且会返回yield后面跟的值
11 print(g.__next__())  # 返回111,并且在此暂停
12 print(g.__next__())  # 遇到下一个__next__继续执行直到遇到下一个yield再次暂停返回值222
13 print(g.__next__())  # yield也可以返回多个值,并且多个值用元组表示

注意:函数只要有yield时即使遇到函数名加括号func(),函数也不会执行,这时候函数已经变为一个生成器,只有调用__next__方法时才会执行

 

生成器实现for循环取值

 1 # 生成器实现for循环
 2 # for i in range(1,10,2):
 3 #     print(i)
 4 
 5 def my_range(x,y,z=1):
 6     while x < y:
 7         yield x
 8         x += z
 9 for i in my_range(1,10,2):
10     print(i)

 

6.yield表达式形式

yield支持外界给其传参

 1 # yield支持外界给他传参
 2 def dog(name):
 3     print('%s好饿' % name)
 4     while True:
 5         food = yield
 6         print('%s吃了%s'%(name,food))
 7 # 当函数体有yield时,函数名加括号不会执行函数,而是将函数变为一个生成器
 8 g = dog('zzz')  # 生成器初始化
 9 g.__next__()  # 需要先运行代码至yield
10 g.send('包子')  # 为yield传参数,并且内置执行__next__
11 g.send('馒头')
12 g.send('骨头')

yield:1.帮你提供了一种自定义生成器的方式

   2.会将你的函数运行状态暂停

   3.可以返回值

和return的异同点

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

  不同点:yield可以返回多次值,return只能返回一次并且直接结束函数

      yield还可以接受外部传的值

 

7.生成器表达式

生成器表达式代码

1 # 生成器表达式
2 res = (i for i in range(100) if i != 3)
3 print(res)  # 生成器初始化
4 # 生成器不会执行任何一行代码,只有遇到__next__才会执行代码
5 print(res.__next__())
6 print(res.__next__())
7 print(res.__next__())
8 print(res.__next__())

面试题

 1 def add(n,i):
 2     return n+i
 3 def test():
 4     for i in range(4):
 5         yield i
 6 g=test()
 7 
 8 for n in [1,10]:
 9     g=(add(n,i) for i in g)
10     # 第一次g = (add(n,i) for i in test())  # 第一次的g是test(),因为test()里有yield,所以不执行
11     # 第二次g = (add(n,i) for i in (add(n,i) for i in test()))  # 第二次的g是第一次的g,所以直接把第一次的g表达式带入就行
12 print(n)
13 res=list(g) # 基于for循环,自带__next__,代码到这才会执行,这时n是第二次的10
14 print(res)

 

8.常用内置方法

(1)abs()求绝对值

res = abs(-89568.56465156)  # 求数字的绝对值
print(res)

 

(2)all(),any()判断多个值的布尔值

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

 

 

(3)locals(),globals().返回名称空间的名字

def index():
    username = '局部名称空间'
    print(locals())  # 在哪就返回哪里的名称空间的名字
index()
print(locals())
print(globals())  # 不管在哪,返回的都是全局名称空间的名字

 

(4)bin(),oct(),hex().进制转化

print(bin(10))  # 10进制转2进制
print(oct(10))  # 10进制转8进制
print(hex(10))  # 10进制转16进制
print(int('0b1010',2))  # 2进制转10进制

 

(5)bool().判断布尔值

1 # 只有0,None,空集合,字典等为False,其他都是True
2 print(bool(2))  # 判断布尔值
3 print(bool(0))

 

(6)bytes().字符串转二进制

1 s = 'sxc'
2 print(bytes(s,encoding='utf-8'))  # 字符串转二进制
3 print(s.encode(encoding='utf-8'))  # 字符串转二进制

 

(7)callable()能否被调用

1 s = 'sxc'
2 def index():
3     pass
4 print(callable(s))  # 能否被调用
5 print(callable(index))  # 暂时只有能加括号的()能被调用

 

(8)chr(),ord().数字与字符在ascii码表的对应关系

print(chr(98))  # 将数字转化为ascii码表对应的字符
print(ord('B'))  # 将字符转化为ascii码表对应的数字

 

(9)dir().返回参数的属性、方法列表

1 l = [1,2,3,4,5]
2 print(dir(l))  # 返回该参数的属性、方法列表

 

(10)divmod().两数相除取整,两数相除取余

1 print(divmod(100,10))  # 两数相除取整,两数相除取余
2 def index(x,y):
3     total_paper,more = divmod(x,y)
4     if more:
5         total_paper += 1
6     print('总共需要%s页'%total_paper)
7 index(101,10)  # 可以解决分页需要页数的问题

 

(11)enumerate()枚举.传入所要枚举的对象和初始值

1 l = ['name','pwd','age']
2 for i,j in enumerate(l,1):  # 枚举,传入所要枚举的对象和初始值
3     print(i,j)

 

(12)eval(),exec().可以简单的执行字符串中编写的代码

1 s = '''
2 print("sxc")
3 '''
4 eval(s)
5 exec(s)

都可以执行字符串中的代码

eval()只能简单的执行,不支持逻辑代码

exec()可以执行复杂的代码

1 s = '''
2 print("sxc")
3 age = 123
4 print(age)
5 '''
6 exec(s)  # exec能执行
7 eval(s)  # eval不能执行

 

(13)format()格式化函数的三种方式

1 # format格式化函数
2 print('{}第一句在这,{}第二句在这'.format(1,2))  # 按位置占位
3 print('{1}第一句在这,{0}第二句在这'.format(1,2))  # 按索引占位
4 print('{x}第一句在这,{y}第二句在这'.format(x = 1,y = 2))  # 按关键字占位

 

(14)help()调出函数的注释

1 def index():
2     '''
3     这是注释
4     :return: 
5     '''
6     pass
7 
8 print(help(index))  # 调出函数的注释

 

(15)isinstance(),判断数据是否是我们后面指定的数据类型

1 n = 'sxc'
2 print(type(n))
3 print(isinstance(n,int))  # 判断数据类型是否为后面指定的那个

 

(16)pow().方法返回 xy(x的y次方) 的值

    round().方法返回浮点数x的四舍五入值

1 print(pow(3,3))  # 3的3次方等于27
2 print(round(10.4))  # 10.4四舍五入等于10

 

9.面向过程编程

面向过程编程:类似于流水线

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

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

一个注册功能按照面向过程编程

 1 # 注册功能
 2 # 获取用户输入
 3 def get_info():
 4     while True:
 5         username = input('请输入您的用户名').strip()
 6         if not username.isalpha():
 7             print('输入有误,请重新输入')
 8             continue
 9         pwd = input('请输入您的密码').strip()
10         re_pwd = input('请再次输入您的密码').strip()
11         if pwd != re_pwd:
12             print('两次密码不一致,请重新输入')
13             continue
14         deal_info(username,pwd)
15         break
16 
17 # 处理用户信息
18 def deal_info(username,pwd):
19     res = '|'.join([username,pwd])
20     save(res,'{username}.txt')
21 
22 # 把用户信息存入文件中
23 def save(res,file_name):
24     with open(file_name,'w',encoding='utf-8') as f:
25         f.write(res)
26 
27 def register():
28     get_info()
29 
30 register()

把一个注册功能分为三步,获取用户输入,处理用户信息和把用户信息存入文件中,这就是面向过程编程.

 13

posted @ 2019-07-15 23:34  瓜落桥下塘  阅读(155)  评论(0编辑  收藏  举报