Python函数部分3——迭代器对象 (不依赖索引取值),三元,列表,字典,生成器表达式等

一、迭代器

    1、什么是迭代

        1.1 迭代是一个重复的过程,每一次重复都是基于上一次的结果而进行的。

        1.2 单纯的重复并不是迭代

    2、为什么要用迭代器

       1、列表,元组,字符串都可以依靠索引把值取出来,而一些数据类型没有索引,那么如何取出里面的值呢? --这里就要用到迭代器!

       2、迭代器就是一种可以不依赖索引的迭代取值方式!

   

二、迭代器分为:可迭代对象不可迭代对象

        2.1、可迭代的对象(数据类型)如下:     在python中,但凡带内置有__iter__()方法的数据类型(或者对象),都是可迭代的对象! 

            1.列表类型  

            2.元组类型 

            3.集合类型  

            4.字典类型  

            5.文本类型(文本类型本身就是迭代器对象"具有__next__()"方法)            

       2.2、不可迭代的对象(数据类型)如下:  

            1.整数类型

            2.浮点数类型

 

二、迭代器对象 (通过调用可迭代对象拿到迭代器对象)  

  2.1 如何拿到一个迭代器对象?

  执行可迭代对象下的__iter__()方法,得到的返回值就是一个迭代器对象

c=['a','b']   #列表类型

res=c.__iter__()
print(res)

>>:<list_iterator object at 0x0000017A993592E8>

 

三、使用迭代器对象   迭代器对象有,内置的__next__()方法 ,同时具有__iter__()方法 (执行该方法得到的仍然是迭代器本身)

     3.1、使用迭代器对象的__next__()方法,就可以取出列表内的值,这就是通过迭代器取值。(不依赖索引取值!!)

     例子1:使用迭代器取出字典里面的key和value

dic={'aaa':123,'bbb':456}
res_dic=dic.__iter__()

迭代获取字典里面的key
print(next(res_dic))  #使用迭代器对象内置__next__方法,可以取出迭代器的值(取第1个key)
print(next(res_dic))  #使用迭代器对象内置__next__方法,可以取出迭代器的值(取第2个key)

aaa
bbb

迭代获取字典里面的value
print(dic[next(res_dic)])   #使用迭代器对象内置__next__方法,可以取出迭代器的值(取第1个value)
print(dic[next(res_dic)])   #使用迭代器对象内置__next__方法,可以取出迭代器的值(取第2个value)

123
456

    例子2:使用迭代器取出文件里面的内容

f=open('a.txt','rt',encoding='utf-8')
iter_obj=f.__iter__()

print(iter_obj.__next__(),end='')   #end='' 表示设置换行符为空
print(iter_obj.__next__(),end='')   #end='' 表示设置换行符为空
print(iter_obj.__next__(),end='')   #end='' 表示设置换行符为空
print(iter_obj.__next__(),end='')   #end='' 表示设置换行符为空

>>:111
>>:22222
>>:33333333
>>:44444444444

    3.2、 当字典内的值被取光了,就会报错"StopIteration" (这是一个结束信号,表示迭代器的值被取完了),如何解决?

    1、使用try+except StopIteration的方式

dic={'k1':1,'k2':1,'k3':1}
res=dic.__iter__()

while True:
    try:
        print(res.__next__())    #使用迭代器对象内置__next__方法,可以取出迭代器的值(取第1个)
    except StopIteration:
        break

k1
k2
k3

 

四、总结迭代器对象

    1、可迭代对象不一定是迭代器对象!

         1.列表类型  2.元组类型 3.集合类型  4.字典类型  都不具备__next__()方法。

    2、迭代器对象一定是可迭代对象!

          1.文本类型(文本类型本身就是迭代器对象"具有__next__()"方法)具备__iter__()方法,同时也具备__next__()方法。

    3、总结迭代器的优缺点

         缺点:1、只能往后取值,不能往前取值,是一次性的,值取干净后无法再次取值,除非重新得到新的迭代器对象。

         优点:1、提供了一种不依赖于索引的迭代取值方式   2、节省内存

 

五、for 循环的底层运行机制  for循环也叫做迭代器循环

   5.1、能被for循环,循环的对象,它一定是可迭代对象!

dic={'k1':1,'k2':1,'k3':1}

for k in dic:
    print(k)

for循环的机制:

   1、先调用in后那个对象的__iter__()方法,得到该对象的迭代器对象:  ### iter_obj=dic.__iter__()
   2、执行迭代器对象的__next__()方法,将得到的返回值赋值给in前面的变量名,然后执行一次循环体代码: ### iter_obj.__next__()
   3、循环往复,直到把dic里面的值全部取完,然后自动捕捉"StopIteration"这个异常,结束循环。

 

六、生成器  (学习生成器是为了掌握一种自定义迭代器的方式)

 6.1、什么是生成器 ?

  1、在函数内但凡有yield关键字在调用函数时就不会执行函数体内的任何代码得到的返回值就是一个生成器对象[一串内存地址](生成器的本质就是迭代器)。

  2、生成器本身就是迭代器。

def func():
    print('11111')
    yield 1
    print('22222')
    print('33333')

res=func()
print(res)

>>:<generator object func at 0x00000204A8F51B48>

 

6.2、使用生成器 -- 用yield来控制返回值(yield和函数内的return很相似),取到想要的值

  nxet(res)过程:

          会触发生成器res所对应的函数体代码的运行,直到遇到yield关键字,把yield后的返回值当做本次next操作的结果返回

def func():
    print('11111')
    yield 1        # 函数体代码运行到此处时会暂停,把yield之前运行的代码以及yield后面的1作为返回值返回
    print('22222')
    yield 2        # 函数体代码运行到此处时会暂停,把yield之前运行的代码以及yield后面的2作为返回值返回
    print('33333')
res=func()

print(next(res))   # 执行next(res)获取到的值为"11111",然后继续执行代码遇到了yield,那么就把yield后面的1作为本次结果的返回值
11111
1

print(next(res))  # 执行next(res)获取到的值为"22222",然后继续执行代码遇到了yield,那么就把yield后面的2作为本次结果的返回值
22222
2

print(next(res))
33333
StopIteration   # 由于函数内的"print('33333')"这行代码之后无yield关键字,那么next(res)在取到'33333'这个值之后,没有其他值可以取了,就会抛出一个StopIteration异常。

for n in res:
    print(n)
11111   # 这是执行函数体代码的结果
1       # 这是for循环变量n的值
22222   # 这是执行函数体代码的结果
2       # 这是for循环变量n的值
33333   # 这是执行函数体代码的结果,由于后面并没有yield关键字,就会抛出一个StopIteration异常。

过程解析:

执行for循环相当于执行了:res.__next__() == next(res):
   1、 首先next(res)会执行遇到yield之前的代码print('11111'),然后拿到"yield 1"把1最为本次结果的返回值赋值给n,然后打印n,那么第一次循环结束。
   2、然后执行next(res),执行函数内"yield 1"之后的代码,直至遇到下一个"yield"关键字。

小例子:自定义函数模拟range(1,10,2)   

def my_range(start,stop,step=1):
    print('开始')
    while start < stop:
        yield start
        start+=step
    print('结束')

for n in my_range(1,10,2):
    print(n)

开始
1
3
5
7
9
结束

 

6.3、生成器如何运行?

  1、首先要明白生成器本身就是迭代器,那么运行生成器就可以按照迭代器的方式去运行!

  2、运行生成器就会触发迭代器所对应的代码,并开始运行迭代器的代码。

  3、运行迭代器的代码时,只要碰到yield,代码就会停止运行,并拿到yield 的返回值。

 

6.4、yield关键字的另外一种使用形式:表达式形式的yield应用

    1、先让函数运行起来,然后暂停到某一处,然后传一个值运行一次代码,传一个值运行一次代码。

   通过:生成器.send的方法给yield 传值,例子如下:[执行g.send就相当于给yield传值,同时执行一次next(g)]


def dog(dog_name):
    print('狗准备开吃!')
    food_list=[]
    while True:
        print(1)
        food=yield food_list   # food=yield='骨头'
        print('狗:%s,吃了%s' %(dog_name,food))
        food_list.append(food)
        print(2)

g=dog('doudou')     #拿到一个迭代器
next(g)             #next(g)==g.send(None),开始运行函数体代码,next(res)==res.__next__()
                    #强调:对于表达式形式的yield的生成器,在使用前必须先用next(g)或者g.send(None)初始化一次。
res2=g.send('骨头')
print(res2)
res3=g.send('骨头1')
print(res3)


狗准备开吃
1
[]
dog: doudou 吃了 骨头
2
1
['骨头']
dog: doudou 吃了 骨头1
2
1
['骨头', '骨头1']

注释:

1、yield后面的 food_list 在没有没传值的情况下,默认为None。

2、执行一次g.send('骨头')后,会把'骨头'传值给 yield ,并执行一次"yield"关键字之后的函数体代码,直到遇见下一个“yield”为止。

3、如上例子,当g.send('骨头')后,循环一遍结束,直到遇见一个新的yield为止。此时 food_list 的值就为None(默认值)。

 

七、面向过程编程

  1、什么是面向过程编程?

   核心是过程二字,过程指的是解决问题的步骤,即先干什么,在干什么,在干什么。。。。

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

 

  优点:将复杂的问题流程化,进而简单化。

  缺点:修改了某一个阶段,其他相关联的阶段都有可能受到影响,牵一发而动全身,扩展性极差。

  应用:应用于对扩展性要求不高的场景。

 

面向过程编程小例子:把要做的事情拆分为一件件小的事情,然后完成。

def login():
    uname=input('pls input uname>>:').strip()
    pwd=input('pls input pwd>>:').strip()
    return uname,pwd

def auth(uname,pwd):
    if uname == 'szq' and pwd == '123':
        return True,uname
    else:
        return False,uname

def index():
    if res[0]:
        print('欢迎%s登录' %res[1])
    else:
        print('%s登录失败' % res[1])

uname,pwd=login()
res=auth(uname,pwd)
index()

 

八、三元表达式

x=10
y=30

res=x if x >y else y   #如果x的值大于y的值,那么返回x,否则返回y的值。
print(res)

30

 

九、列表生成式,字典生成式,生成器表达式

 当数据量不多的时候,用列表表达式

 1、列表生成式 -- 直接将产生的结果放到列表里面

list=[n for n in range(10)]   # 每一次for循环取到的值,都会赋值给给左边的n,然后放在列表list里面,然后接着下一次循环
print(list)

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

 for 循环后接if判断

list=[n for n in range(10) if n > 3]   # 每一次for循环取到的值(然后执行右边的if判断,符合要求的值),都会赋值给给左边的n,然后放在列表list里面,然后接着下一次循环
print(list)

[4, 5, 6, 7, 8, 9]

 例子1:将小写全部改为大写

names=['aaa','bbb','ccc','ddd']
names=[name.upper() for name in names]
print(names)

['AAA', 'BBB', 'CCC', 'DDD']

 例子2:将列表内以'sb'结尾的字符去除,并统计剩下字符的长度

names=['aaa','bbb_sb','ccc_sb','ddd']
names=[len(name) for name in names if not name.endswith('sb')]
print(names)

[3, 3]

 

2、字典生成式

 例子:将字典里面的元组改为key:value的形式

dic={('aaa','123'),('bbb','223'),('ccc','323')}
dic={k:y for k,y in dic}
print(dic)

{'aaa': '123', 'ccc': '323', 'bbb': '223'}

 

3、生成器表达式  -- 拿到一个生成器对象,需要next(g)一次取一次值。

当数据量特别多的时候用,用生成器表达式

l=(n for n in range(10))
print(next(l))
print(next(l))
print(next(l))

0
1
2

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章