可迭代对象 迭代器对象 生成器表达式 生成器yield函数


''''''
'''
我们在用for ... in ...语句循环时,in后面跟随的对象要求是可迭代对象,
即可以直接作用于for循环的对象统称为可迭代对象(Iterable),如list、tuple、dict、set、str等。
可迭代对象是实现了__iter__()方法的对象,而迭代器(Iterator)则是实现了__iter__()和__next__()方法的对象,可以显示地获取下一个元素。
这种可以被next调用并不断返回下一个值的对象称为迭代器。
迭代器一定是可迭代对象,反过来则不一定成立。
用iter()函数可以把list、dict、str等Iterable变成Iterator,
例如:
bb=[x for x in range(10)]
cc=iter(bb)
cc.next()
循环变量的值其实可以看着是一次次用next取值的过程,每取一个值,做一次处理。
list等对象用于循环实际上可以看着是用iter()方法产生一个迭代器,然后循环取值。

生成器(generator)就是一个能返回迭代器的函数,其实就是定义一个迭代算法,可以理解为一个特殊的迭代器。
调用这个函数就得到一个迭代器,生成器中的yield相当于一个断点,执行到此返回一个值后暂停,从而实现next取值。
'''

"""
什么是迭代器
# 可迭代对象执行内置的__iter__方法得到就是该对象的迭代器对象
迭代:更新换代(重复)的过程,每次的迭代都必须基于上一次的结果
迭代器和简单的重复循环的区别:每次迭代与上一次的结果是否有关系
没关系就是见到那的循环重复,有关系 就是迭代
迭代器:迭代取值的工具

为什么要用
迭代器给你提供了一种 不依赖于索引取值 的方式

如何用
"""

# 不算 因为只是简单的重复
# n = 0
# while True:
# print(n)


# 重复 + 每次迭代都是基于上一次的结果而来的
# l = [1,2,3,4]
# s = 'hello'
# n = 0
# while n < len(s):
# print(s[n])
# n += 1
"""
需要迭代取值的数据类型
字符串
列表
元组
字典
集合
"""


"""
# 可迭代对象
# 只有内置有__iter__方法的都叫做可迭代对象
# 可迭代对象执行内置的__iter__方法得到就是该对象的迭代器对象


补充:针对双下线开头双下划线结尾的方法
推荐读:双下+方法名

基本数据类型中
是可迭代对象的有
str list tuple dict set
文件对象(执行内置的__iter__之后还是本身 没有任何变化):文件对象本身就是迭代器对象

"""
# n = 1 # int
# f = 1.1 # float
# s = 'hello' # str
# l = [1, 2, 34, ] # list
# t = (1, 2, 34) # tuple
# s1 = {1, 2, 3, 4} # set
# d = {'name': 'jason'} # dict
# f1 = open('xxx.txt', 'w', encoding='utf-8') # 文件句柄
#
# print(s.__len__()) # 简化成了len(s)
#
#
# res = s.__iter__() # res = iter(s) 字符串可迭代 有__iter__方法
# res1 = l.__iter__() # res1 = iter(l) 列表可迭代 字符串可迭代 有__iter__方法
# res2 = f1.__iter__() # res2 = iter(f1) 文件句柄可迭代 字符串可迭代 有__iter__方法
# print(res,res1,res2)
# print(f1)







"""
迭代器对象
# 可迭代对象执行内置的__iter__方法得到就是该对象的迭代器对象
1.内置有__iter__方法 变成迭代器方法,跟自己原来是一样的 is判断为True
2.内置有__next__方法 取值方法
ps: 迭代器一定是可迭代对象
而可迭代对象不一定是迭代器对象
迭代器取值的特点
1.只能往后依次取 不能后退
"""



# l = [1,2,3,4]
# # 生成一个迭代器对象
# iter_l = l.__iter__()
#
# # 迭代器取值 调用__next__
# print(iter_l.__next__()) # 1
# print(iter_l.__next__()) # 2
# print(iter_l.__next__()) # 3
# print(iter_l.__next__()) # 4
# # print(iter_l.__next__()) # 如果取完了 直接报错


# d = {'name':'jason','password':'123','hobby':'泡m'}
# # 将可迭代对象d转换成迭代器对象
# iter_d = d.__iter__() # 直接调用方法 可迭代对象.__iter__() = 迭代器对象
# # 迭代器对象的取值 必须用__next__ 可迭代对象.__iter__().__next__() 可以这样从迭代器取值
# d.__iter__().__next__() # 可迭代对象.__iter__().__next__() 可以这样从迭代器取值
# print(iter_d.__next__())
# print(iter_d.__next__())
# print(iter_d.__next__())
# print(iter_d.__next__()) # 取完了 报错StopIteration

f1 = open('xxx.txt','r',encoding='utf-8')
# 调用f1内置的__iter__方法
# iter_f = f1.__iter__()
# print(iter_f is f1)
"""
迭代器对象无论执行多少次__iter__方法得到的还是迭代器对象本身(******)
"""
# print(f1 is f1.__iter__().__iter__().__iter__().__iter__())
"""
问:__iter__方法就是用来帮我们生成迭代器对象
而文件对象本身就是迭代器对象,为什么还内置有__iter__方法???
因为迭代器对象也支持for循环,需要这个iter方法,作为可迭代对象,迭代器也是可迭代对象,如果没有这个方法,就迭代器不能for循环了
"""


# d = {'name':'jason','password':'123','hobby':'泡m'}
#
# print(d.__iter__().__next__()) # name 因为每一次都调用iter制造了一个新的迭代器,每次取值都是第一次取值
# print(d.__iter__().__next__()) # name 因为每一次都调用iter制造了一个新的迭代器,每次取值都是第一次取值
# print(d.__iter__().__next__()) # name 因为每一次都调用iter制造了一个新的迭代器,每次取值都是第一次取值
#
# iter_d = d.__iter__()
# print(iter_d.__next__()) # name 每次取到的值不一样,是依次取值,这个是因为迭代器是同一个迭代器取值三次
# print(iter_d.__next__()) # password 每次取到的值不一样,是依次取值,这个是因为迭代器是同一个迭代器取值三次
# print(iter_d.__next__()) # hobby 每次取到的值不一样,是依次取值,这个是因为迭代器是同一个迭代器取值三次
# # print(iter_d.__next__()) # 取值结束,超过最大数量 会报错

# # 异常处理
# d = {'name':'jason','password':'123','hobby':'泡m'} # 定义一个字典
# iter_d = d.__iter__() # 调用方法,将字典这个可迭代对象做成迭代器
# while True: # while循环取值
# try: # 尝试循环取值
# print(iter_d.__next__()) # 打印 用字典的迭代器调用发给发next取值
# except StopIteration: # 如果碰到 报错为 StopIteration
# # print('老母猪生不动了')
# break # 退出while循环 不会报错
#

# # 文件句柄循环取值,用异常捕获来使超出取值范围之后不报错 同理上
# f = open('xxx.txt','r',encoding='utf-8') # 值作文件句柄
# iter_f = f.__iter__() # 将文件句柄这个可迭代对象通过调用iter变成迭代器
# # print(iter_f.__next__()) # 这样一行行取值,超过取值范围会报错
# # print(iter_f.__next__())
# # print(iter_f.__next__())
# # print(iter_f.__next__())
# # print(iter_f.__next__())
# while True: # while循环取值
# try: # 尝试循环取值
# print(iter_f.__next__()) # 打印 用文件句柄的迭代器调用发给发next取值
# except StopIteration: # 如果碰到 报错为 StopIteration
# print('老母猪生不动了') # 打印字符串
# break # 退出while循环




"""
迭代器取值的特点
1.只能往后依次取 不能后退
"""


# # for循环本质
# d = {'name': 'jason', 'password': '123', 'hobby': '泡m'}
# # for i in d:
# # print(i)
# # # for循环后面的in关键 跟的是一个可迭代对象 d是一个可迭代对象
#
# for i in 1: # TypeError: 'int' object is not iterable 整型不是可迭代对象 in后面需要跟可迭代对象
# pass
# iter(1)
"""
for循环内部的本质
1.将in后面的对象调用__iter__转换成迭代器对象
2.调用__next__迭代取值
3.内部有异常捕获StopIteration,当__next__报这个错 自动结束循环
"""

# map zip 方法都是基于for循环
# l = [1,2,3,4]
# res = map(lambda x:x+1,l) # map的内部原理就是基于for循环,for循环如上,就是嗲用iter做成迭代器,用next取值
# print(map(lambda x:x+1,l)) # <map object at 0x000001D406758A20> python3中是一个迭代器
# print(res.__next__()) # 可以通过调用next的方法来取值
# print(res.__next__())
# print(res.__next__())


# l1 = [1, 2, 3, 4, 5]
# l2 = ['a', 'b', 'c']
# print(zip(l1, l2)) # <zip object at 0x000001D406768688> zip方法是基于for循环,返回的是一个迭代器
# res2 = zip(l1, l2) # 将迭代器赋值给res2
# print(res2.__next__()) # (1, 'a') 可以通过调用next来取值,zip将依次从不同容器中的取出取值,将他们组成一个元组,返回


"""
可迭代对象:内置有__iter__方法的
迭代器对象:既内置有__iter__也内置有__next__方法

迭代取值:
优点
1.不依赖于索引取值
2.内存中永远只占一份空间,不会导致内存溢出

缺点
1.不能够获取指定的元素
2.取完之后会报StopIteration错

"""


"""
生成器:用户自定义的迭代器,本质就是迭代器
# 函数内如果有yield关键字,那么加括号执行函数的时候并不会触发函数体代码的运行,加括号运行后会返回一个迭代器
# yield后面跟的值就是调用迭代器__next__方法你能得到的值
# yield既可以返回一个值也可以返回多个值 并且多个值也是按照元组的形式返回
def func(): # 函数内如果有yield关键字,那么加括号执行函数的时候并不会触发函数体代码的运行
print('first')
yield 666 # yield后面跟的值就是调用迭代器__next__方法你能得到的值
# g = func() # 生成器初始化:将函数变成迭代器 g就是迭代器 ,有yield的函数,加括号运行后会返回一个迭代器
# print(g.__next__()) # 可以这样从g这个迭代器中取值
"""

# def func():
# print('first')
# yield 666 # 函数内如果有yield关键字,那么加括号执行函数的时候并不会触发函数体代码的运行
# print('second')
# yield 777
# print('third')
# yield 888
# print('forth')
# yield # 暂停在这 会打印 forth None
# yield # 暂停在这 会打印 None
# # yield后面跟的值就是调用迭代器__next__方法你能得到的值
# # yield既可以返回一个值也可以返回多个值 并且多个值也是按照元组的形式返回
# g = func() # 生成器初始化:将函数变成迭代器
# print(g) # <generator object func at 0x00000246A3EDEFC0> 迭代器地址
# print(g.__next__()) # first 666 print自带换行 打印是两行
# print(g.__next__()) # second 777 print自带换行 打印是两行
# print(g.__next__()) # third 888 print自带换行 打印是两行
# print(g.__next__()) # forth None print自带换行 打印是两行
# print(g.__next__()) # None print自带换行 打印是两行




# # 内置函数range()
# print(range(1,10)) # 内置函数python给提供的
# # range()的使用
# for i in range(1,10,2): # 从1开始到10 ,步长为2
# print(i) # 结果为 1 3 5 7 9 ,peint自带换行

# # 自己写一个这样的 功能
# def my_range(start,end,step=1): # 定义函数 有三个形参,其实,结束,步长
# while start < end: # while循环,如果开始小于结束
# yield start # 则返回 开始值
# start += step # 开始值 自增长 步长
#
# for j in my_range(1,100,2): # 打印从1到100,步长为2,所有奇数,
# # 开始为1,如果1小于100,返回1,开始值变为1增加步长2,
# # 开始为3,循环3小于100,则打印3,开始值变为3增加步长2,
# # 开始为5,继续循环
# print(j)




# # yield支持外界为其传参
# def dog(name):
# print('%s 准备开吃'%name)
# while True:
# food = yield
# print('%s 吃了 %s'%(name,food))
# # 当函数内有yield关键字的时候,调用该函数不会执行函数体代码,而是将函数变成生成器
# g = dog('egon') # 调用函数dog,则返回一个生成器,即将dog变为一个自己定义的迭代器,即生成器,打印字符串egon 准备开吃
# res = g.__next__() # 必须先将代码运行至yield 才能够为其传值,g就是生成器
# print(res) # None 调用生成器取值,yield后面没有值返回None
# g.send('狗不理包子') # 给yield左边的变量传参 触发了__next__方法,
# # 用send发送参数,send给迭代器调用next返回的第一个值赋值'狗不理包子' ,赋值给yield的返回值,然后执行函数中的打印,在循环至yield暂停
# g.send('饺子')
# # 用send发送参数,send给迭代器调用next返回的第一个值赋值'饺子' ,赋值给yield的返回值,然后执行函数中的打印,在循环至yield暂停


"""
yield
1.帮你提供了一种自定义生成器方式
2.会帮你将函数的运行状态暂停住
3.可以返回值

与return之间异同点
相同点:都可以返回值,并且都可以返回多个
不同点:
yield可以返回多次值,而return只能返回一次函数立即结束
yield还可以接受外部传入的值
"""



'''
列表生成式 和 生成器表达式
'''
# # 列表生成式
# res = [i for i in range(1,10) if i != 4] # [1, 2, 3, 5, 6, 7, 8, 9]
# print(res) # 列表生成式生成一个列表 [1, 2, 3, 5, 6, 7, 8, 9]

# 生成器表达式
# res1 = (i for i in range(1,100000000) if i != 4) # 生成器表达式
# print(res1) # <generator object <genexpr> at 0x0000023B7FF5EFC0> 生成一个生成器
# print(res1.__next__()) # 1
# print(res1.__next__()) # 2
# print(res1.__next__()) # 3
# print(res1.__next__()) # 4
# print(res1.__next__()) # 5
# print(res1.__next__()) # 6
# print(res1.__next__()) # 7
"""
# 生成器不会主动执行任何一行代码
# 必须通过__next__触发代码的运行
"""



# # 占内存
# f = open('xxx.txt','r',encoding='utf-8') #打开文件,做文件句柄
# data = f.read() # 读取所有内容到内存
# print(len(data)) # 打印数据长度
# f.close() # 关闭文件


# # 计算文件内字符个数
# with open('xxx.txt','r',encoding='utf-8') as f: # 只读模式打开一个文件 文件句柄为f
# # n = 0 # 设置变量n = 0
# # for line in f: # 循环取值,从文件句柄中一行行读出内容
# # n += len(line) # 计算循环取出的一行行的字符长度,即字符个数,累加给n
# # print(n) # 打印n 可以计算出字符个数
# g = (len(line) for line in f) # 三元表达式,循环取出line,交给内置函数len 相当与一下一行行取值
# # for循环的原理就是将文件句柄f,通过调用用iter做成迭代器,然后可以用next循环取值
# # print(g.__next__()) # 4 换行符\n也算一个值 文件中有三行,每行三个数字,取值len为4是因为有一个是\n换行符
# # print(g.__next__()) # 4
# # print(g.__next__()) # 4
# print(sum(g)) # 求和函数,g是迭代器,求迭代器中所有值的和,即每一行len结果的和
# # 如果上面三步取值了,则sum再取就是继续迭代取值后面的,有剩余的值就是剩余的值,没有就是0,最后一行没有换行符





#
# # 案例 ;
#
# def add(n,i): # 定义函数 add() 需要两个参数
# return n+i # 返回两个参数相加
# def test(): # 定义一个函数test()
# for i in range(4): # 循环从0123中取值
# yield i # 迭代器每次返回一个值
# g=test() # 将函数test变成迭代器g,即test加__iter__,g可以调用next
# # 到此只是生成了一个生成器 g = test() 能给出来的值为(0123),但是没有调用生成器,没有next
# for n in [1,10]: # 循环从列表中取值 列表中只有两个值
# g=(add(n,i) for i in g)
# print(n) # 10
# res=list(g)
# print(res) # [20, 21, 22, 23]
"""
list的本质时for循环生成器取值,到res=list(g)这一步会执行g生成器中的所有代码

for i in (add(10,i) for i in test()): # 会执行所有的生成器内部的代码
add(n,i)
"""

'''
第一步 定义函数 add() 需要两个参数
返回两个参数相加
第二步 定义一个函数test()
循环从0123中取值
迭代器每次返回一个值
第三步 g=test() # 将函数test变成迭代器g,即test加__iter__,g可以调用next
# 到此只是生成了一个生成器 g = test() 能给出来的值为(0123),但是没有调用生成器,没有next

将函数test变成迭代器g,即test加__iter__,g可以调用next,或者被for循环
第四步 第一次for循环g=(add(n,i) for i in test())
因为n的取值列表中只有两个值1和10,循环两次,循环到10 结束后 : n = 10 ,在执行下面的步骤
g =(add(n,i) for i in test()) 第一次循环的结果是一个生成器表达式g(第一次for循环等号左边的g)
此时第一次for循环 循环两次结束后 : n = 10 产生的生成器g(第一次for循环等号左边的g)不运行,因为没有next,因为没有运行
第一次for循环 循环两次循环两次结束后 : n = 10 中的for i in后面的g(第一次for循环等号右边的g)是test()生成器,自定义的迭代器test()
并且产生一个名字也是g(第一次for循环等号左边的g)的生成器
第一次for循环 循环两次结束后 : n = 10 g=(add(n,i) for i in test())
g =(add(n,i) for i in test())
第一次循环循环两次结束后 : n = 10 的结果是一个生成器表达式g(第一次for循环等号左边的g)
第五步 第二次for循环 循环两次后 结束后 : n = 10 g(第二次for循环等号右边的g),是第一次for循环的生成器g(第一次for循环等号左边的g),
会产生一个名字也是g(第二次for循环等号右边的g)的生成器
第二次for循环 循环两次后 结束后 : n = 10 g=(add(n,i) for i in (add(n,i) for i in test()))
g=(add(n,i) for i in (add(n,i) for i in test()))
因为n的取值列表中只有两个值1和10,循环两次,循环到10,结束后 : n = 10 在执行下面的步骤,print(n)可以查看到n = 10
第二次for循环 循环两次结束后 : n = 10 g=(add(n,i) for i in (add(n,i) for i in test()))
现在为止还没调用这个生成器,g就是一个生成器表达式(生成器)
第六步 接下来执行res = res=list(g),原理是for循环从生成器g中取值,现在带入n = 10,
g=(add(10,i) for i in (add(10,i) for i in test())) 当i等于 0 1 2 3 时,g给出的值分别为
i = 0 g = 10 + (10 + 0) g = 20
i = 1 g = 10 + (10 + 1) g = 21
i = 2 g = 10 + (10 + 2) g = 22
i = 3 g = 10 + (10 + 3) g = 23
第七步 列表res = [20, 21, 22, 23]
'''


# 常用内置方法
'''
abs() # 求绝对值,括号里添加数字(只能加一个)
bin(10) # 十进制转二进制,括号里写十进制
oct(10) # 十进制转八进制,括号里写十进制
hex(10) # 十进制转十六进制,括号里写十进制
int('0b1010',2) # 其他进制转十进制,括号第一个值写其他进制,第二个参数写第一个值是什么进制要转十进制
bool(1) # 判断的布尔值,括号里可以写int float str set tuple dict 返回布尔值
bytes(s,encoding='utf-8') # 将字符串转为bytes类型 返回b开头的二进制
callable(index) # 判断是否可调用,括号可以写函数名,变量名,返回布尔值
chr(97) # 查看ASCII码表中第97位对应的字符,括号内写数字 ASCII码表 数字转字符
ord('a') # 查看ASCII码表中该字符对应的数字,括号内写字符 ASCII码表 字符转数字
dir(l) # 查看改数据可以用的方法,括号内写数据或者变量名
divmod(101,10) # 计算第一个数除以第二个数,返回元组,元组第一个值位熵值,第二个位余数,可以用作分页器
format 三种玩法() # {}占位 {index} 索引 {name} 指名道姓 格式化输出
enumerate() 枚举 # for i,j in enumerate(l,1):print(i,j) 括号第一个值为列表,第二个值为枚举索引从多少开始,不写默认从0开始
eval() # 识别字符串中的代码,引号引起来里面的代码, eval不支持逻辑代码,只支持一些简单的python代码
exec() # 识别字符串中的代码,引号引起来里面的代码, 支持逻辑代码
globals() # 查看全局名称空间中的现存变量和值,在全局名称空间在局部名称空间都能看到
locals() # 查看当前局部名称空间中的现存变量和值,在全局就看的是全局一样的,在局部就是看局部
help(login) # 查看函数注释,括号内写函数名
isinstance(n,list) # 查看某个数据的数据类型是不是什么类型,第一个参数为数据,第二个参数为数据类型,返回 True 或者False
round(3.4) # 四舍五入,括号内写数值,返回四舍五入的结果
pow(2,3) # 算次方,第一个参数为底数,第二个参数为指数,表示2的三次方
'''


"""
面向对象需要学习的方法
classmethod
delattr
getattr
hasattr
issubclass
property
repr
setattr
super
staticmethod

"""




# print(abs(-11)) # 求绝对值
# # l = [0,1,0]
# # print(all(l)) # 只要有一个为False就返回False
# # print(any(l)) # 只要有一个位True就返回True
# def index():
#
# username = '我是局部名称空间里面的username'
# # print(locals()) # 当前语句在哪个位置 就会返回哪个位置所存储的所有的名字
# print(globals()) # 无论在哪 查看的都是全局名称空间
# index()
# print(bin(10))
# print(oct(10))
# print(hex(10))
# print(int('0b1010',2))


# print(bool(1.1))
# print(bool((1,)))


# s = 'hello'
# print(s.encode('utf-8'))
# print(bytes(s,encoding='utf-8'))

# 可调用的(可以加括号执行相应功能的)
# l = [1,2,3]
# def index():
# pass
# print(callable(l))
# print(callable(index))





# print(chr(97)) # 将数字转换成ascii码表对应的字符
# print(ord('a')) # 将字符按照ascii表转成对应的数字

# dir获取当前对象名称空间里面的名字
# l = [1,2,3]
# print(dir(l))
#
# import test
# print(dir(test))
# print(test.name)

# divmod 分页器

# print(divmod(101,10))
# total_num,more = divmod(900,11)
# if more:
# total_num += 1
# print('总页数:',total_num)

# # enumerate 枚举
# l = ['a','b']
# for i,j in enumerate(l,1):
# print(i,j)

# eval exec
# s ="""
# print('hello baby~')
# x = 1
# y = 2
# print(x + y)
# """
# eval(s)
# exec(s)

# eval不支持逻辑代码,只支持一些简单的python代码
# s1 = """
# print(1 + 2)
# for i in range(10):
# print(i)
# """
# eval(s1)
# exec(s1)


# name = 'jason'
# s2 = """
# name
# """
# print(eval(s2))

# format 三种玩法
# {}占位
# {index} 索引
# {name} 指名道姓

# print(globals())
# def login():
# """
# 一起嗨皮
# :return:
# """
# print(help(login))
#
# # isinstance 后面统一改方法判断对象是否属于某个数据类型
# n = 1
# print(type(n))
# print(isinstance(n,list)) # 判断对象是否属于某个数据类型

# print(pow(2,3))


# print(round(3.4))











































































猜你喜欢

转载自www.cnblogs.com/xiaozhenpy/p/11220585.html
今日推荐