Python之旅:迭代器、生成器、面向过程编程

迭代器

迭代器概念

1
2
3
4
5
6
7
8
9
10
##迭代器即迭代的工具,那什么是迭代呢?
##迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值
while True: #只是单纯地重复,因而不是迭代
print('===>')

l=[1,2,3]
count=0
while count < len(l): #迭代
print(l[count])
count+=1

可迭代对象、迭代器对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
##1、为何要有迭代器?
对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器
##2、什么是可迭代对象?
字面意思分析:可以重复的迭代的实实在在的东西。
专业角度: 内部含有'__iter__'方法的对象,就是可迭代对象。
可迭代对象指的是内置有__iter__方法的对象,即obj.__iter__,如下
'hello'.__iter__
(1,2,3).__iter__
[1,2,3].__iter__
{'a':1}.__iter__
{'a','b'}.__iter__
open('a.txt').__iter__

##3、什么是迭代器对象?
字面意思:可以重复迭代的工具。
专业角度: 内部含有'__iter__'并且含有"__next__"方法的对象,就是迭代器
可迭代对象执行obj.__iter__()得到的结果就是迭代器对象
而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象

文件类型是迭代器对象
open('a.txt').__iter__()
open('a.txt').__next__()

##4、注意:
迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象

迭代对象的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
dic={'a':1,'b':2,'c':3}
iter_dic=dic.__iter__() #得到迭代器对象,迭代器对象即有__iter__又有__next__,但是:迭代器.__iter__()得到的仍然是迭代器本身
iter_dic.__iter__() is iter_dic #True

print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
## print(iter_dic.__next__()) #抛出异常StopIteration,或者说结束标志

##有了迭代器,我们就可以不依赖索引迭代取值了
iter_dic=dic.__iter__()
while 1:
try:
k=next(iter_dic)
print(dic[k])
except StopIteration:
break

##这么写太丑陋了,需要我们自己捕捉异常,控制next,python这么牛逼,能不能帮我解决呢?能,请看for循环

for循环

1
2
3
4
5
6
7
8
9
##基于for循环,我们可以完全不再依赖索引去取值了
dic={'a':1,'b':2,'c':3}
for k in dic:
print(dic[k])

##for循环的工作原理
##1:执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic
##2: 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码
##3: 重复过程2,直到捕捉到异常StopIteration,结束循环

迭代器的优缺点

优点:

  • 提供一种统一的、不依赖于索引的迭代方式
  • 惰性计算,节省内存

缺点:

  • 无法获取长度(只有在next完毕才知道到底有几个值)
  • 一次性的,只能往后走,不能往前退

生成器

生成器介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
## 生成器:
## 函数内包含有yield关键字,
## 再调用函数,就不会执行函数体代码,拿到的返回值就是一个生成器对象
def func():
print('====>first')
yield 1
print('====>second')
yield 2
print('====>third')
yield 3
print('====>end')

obj=func()
print(obj) # <generator object func at 0x0000000002170FC0>
## 生成器本质就是迭代器,也就是说生成器的玩法其实就是迭代器的玩法
print(obj.__iter__() is obj) # >> True;
print(obj.__next__())
print(obj.__next__())
print(obj.__next__())
## ====>first
## 1
## ====>second
## 2
## ====>third
## 3
print(obj.__next__()) # 此时迭代对象已经没有值了就会停止迭代StopIteration

生成器就是迭代器

1
2
3
4
5
obj.__iter__
obj.__next__
##2、所以生成器就是迭代器,因此可以这么取值
res=next(obj)
print(res)

实例演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
## 实例1:for循环迭代
## 1、iter_obj=obj.__iter__(),拿到迭代器
## 2、出发iter_obj.__next__(),拿到该方法的返回值,赋值给item
## 3、周而复始,直到函数内不在有yield,即取值完毕
## 4、for会检测到StopIteration异常,结束循环
def func():
print('====>first')
yield 1
print('====>second')
yield 2
print('====>third')
yield 3
print('====>end')

obj = func()
for item in obj:
print(item)

## 实例2:用工具实现一个无穷数字
def my_range():
n=0
while True:
yield n
n+=1

res=my_range()
print(res.__next__()) # 执行到 yield 0
print(res.__next__()) # 继续往下走n+=1 在到yield 1
print(res.__next__()) # 执行到 yield 2
print(res.__next__()) # 以此类推

## 实例3:模仿rang
def my_range(start,stop,stemp=2):
n=start
while n<stop:
yield n
n+=stemp

for r in my_range(1,10):
print(r)

表达式yield的应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
## 模拟上菜
def eat(name):
print('{} 准备吃饭了'.format(name))
food_list=[]
while True:
# 第一次__next__ 停在这 yield收到值为 None; food=yield=None
# 第二次__next__ 停在这 yield收到值为 红烧肉;food=yield=红烧肉
# 第三次__next__ 停在这 yield收到值为 大盘鸡;food=yield=大盘鸡
food=yield food_list # 每次执行到把food_list返回去
print('{} 吃了 {}'.format(name,food))
food_list.append(food)
## 1、 必须初始化以此,让函数停在yield的位置
ff=eat('刘清政')
ff.__next__()

## 2、 接下来就是上菜
## send 有两个方面功能
## 1: 给yield传值
## 2: 同__next__的功能
print(ff.send('红烧肉'))
print(ff.send('大盘鸡'))
print(ff.send('大米'))

#### 输出如下 ###
刘清政 准备吃饭了
刘清政 吃了 红烧肉
['红烧肉']
刘清政 吃了 大盘鸡
['红烧肉', '大盘鸡']
刘清政 吃了 大米
['红烧肉', '大盘鸡', '大米']

总结

总结yield:

1、为我们提供了一种自定义迭代器的方式,
可以在函数内用yield关键字,调用函数拿到的结果就是一个生成器,生成器就是迭代器

2、yield可以像return一样用于返回值,区别是return只能返回一次值,而yield可返回多次
因为yield可以保存函数执行的状态

面向过程编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
##1、首先强调:面向过程编程绝对不是用函数编程这么简单,面向过程是一种编程思路、思想,而编程思路是不依赖于具体的语言或语法的。言外之意是即使我们不依赖于函数,也可以基于面向过程的思想编写程序

##2、定义
面向过程的核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么

基于面向过程设计程序就好比在设计一条流水线,是一种机械式的思维方式

##3、优点:复杂的问题流程化,进而简单化

##4、缺点:可扩展性差,修改流水线的任意一个阶段,都会牵一发而动全身

##5、应用:扩展性要求不高的场景,典型案例如linux内核,git,httpd

##6、举例
流水线1
用户输入用户名、密码--->用户验证--->欢迎界面

流水线2
用户输入sql--->sql解析--->执行功能

ps:函数的参数传入,是函数吃进去的食物,而函数return的返回值,是函数拉出来的结果,面向过程的思路就是,把程序的执行当做一串首尾相连的功能,该功能可以是函数的形式,然后一个函数吃,拉出的东西给另外一个函数吃,另外一个函数吃了再继续拉给下一个函数吃。。。

示例:复杂的问题变得简单,但扩展功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
##1、步骤一:拿到用户输入的合法的信息:用户名、密码、余额、年龄
db_path='db.txt'

def get_uname():
while True:
uname=input('用户名>>:').strip()
if not uname.isalpha():
print('\033[45m用户名必须为英文字母...\033[0m')
continue
with open(r'%s' %db_path,'r',encoding='utf-8') as f:
for line in f:
uinfo=line.strip('\n').split(',')
if uname == uinfo[0]:
print('\033[45m用户名已存在...\033[0m')
break
else:
return uname

def get_pwd():
while True:
pwd1=input('请输入密码>>: ').strip()
pwd2=input('再次输入密码>>: ').strip()
if pwd1 == pwd2:
return pwd1
else:
print('\033[45m两次输入的密码不一致,请重新输入...\033[0m')

def get_bal():
while True:
bal=input('请输入余额: ').strip()
if bal.isdigit():
# bal=int(bal)
return bal
else:
print('\033[45m钱必须是数字,傻叉...\033[0m')

def get_age():
pass

##2、步骤二:写入文件
def file_hanle(uname,pwd,bal,age):
with open(r'%s' %db_path,'a',encoding='utf-8') as f:
f.write('%s,%s,%s,%s\n' %(uname,pwd,bal,age))

## 注册功能
def register():
#步骤1:
uname=get_uname() #拿到合法的用户名
pwd=get_pwd() #拿到合法的密码
bal=get_bal() #拿到合法的余额
#步骤2:
file_hanle(uname,pwd,bal) #写入文件