迭代器 for循环的本质 生成器

迭代器

迭代:更新换代的过程,每次的迭代都必须基于上一次的结果

迭代器:迭代取值的工具 

作用

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

根据以上对于迭代的描述,如果只是简单的重复,不算迭代,如下:

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__()方法的都叫做可迭代对象

  # 对于这种 双下划线开头 双下划线结尾的方法

  # 我们读作“双下+方法名”

 

基本数据类型中,是可迭代对象的有:字符串(str)、列表(list)、元组(tuple)、字典(dict)、集合(set)

  另外 文件对象(file) 是可迭代对象,也是迭代器对象

str0 = 'bitten'
str0.__iter__()
print(type(str0))  # 字符串

list0=[1,2,3,]
list0.__iter__()
print(type(list0))  # 列表

tuple0 = (1,2,3)
tuple0.__iter__()
print(type(tuple0))  # 元组

dict0={'name':'bitten'}
dict0.__iter__()
print(type(dict0))  # 字典

set0={1,2,354,}
set0.__iter__()
print(type(set0))  # 集合
f1 = open('xxx.txt','w',encoding='utf-8')
res = f1.__iter__().__iter__().__iter__().__iter__()
print(f1 is res)  # True
# 文件对象(执行内置的__iter__之后还是本身 没有任何变化):文件对象本身就是迭代器对象

迭代器对象

  可迭代对象执行内置的__iter__方法得到的就是该对象的迭代器对象

  迭代器对象需要满足的条件:

  1.内置有__iter__方法
  2.内置有__next__方法
  

    ps:迭代器一定是可迭代对象  
    而可迭代对象不一定是迭代器对象

  

  迭代器对象取值方法:

  必须调用__next__()方法

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__())  # 如果取完了 直接报错StopIteration
异常处理

#把上面这五行print改成下面的形式

# while True:
#     try:
#         print(iter_l.__next__())
#     except StopIteration:
#         # print('值已近取完啦')
#         break

  迭代器对象无论执行多少次__iter__()方法得到的还是迭代器对象本身

  ps:文件对象作为迭代器对象,还内置了__iter__()

    因为for循环后面in(如果迭代器对象没有__iter__)

    是因为只有使用了__iter__()方法才能继续用__next__()取值

  迭代器取值的特点
  1. 只能往后依次取 不能返回取前面的值

for循环的本质

d = {'name':'bitten','password':'123','hobby':'hiking'}
for i in d:
    print(i)
# for循环后面的in关键 跟的是一个可迭代对象

  for循环内部的本质
    1.将in后面的对象调用__iter__转换成可迭代对象

        上面👆这条就可以解释为什么文件对象是迭代器对象但是还是内置有__iter__方法

        因为如果没有的话,在执行上面👆👆的第1.步的时候就会报错。

    2.将这个可迭代对象,作为迭代器对象,调用__next__迭代取值
    3.内部有异常捕获StopIteration,当__next__报这个错 自动结束循环

 

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

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

生成器

  即用户自定义的迭代器

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

用生成器写range函数

for i in range(1,10,2):
    print(i)
#1 3 5 7 9
def my_range(start,end,step=1):
    while start < end:
        yield start
        start += step
# 这只是完成了range的功能中的一部分

for j in my_range(1,100,2):
    print(j)

补充(yield的表达式形式)(了解)

def dog(name):
    print('%s 准备开吃'%name)
    while True:
        food = yield
        print('%s 吃了 %s'%(name,food))

# 当函数内有yield关键字的时候,调用该函数不会执行函数体代码
# 而是将函数变成生成器
g = dog('lucky')
g.__next__()  # 必须先将代码运行至yield 才能够为其传值
g.send('狗不理包子')  # 给yield左边的变量传参  触发了__next__方法
g.send('饺子')
g.send('肉夹馍')

>>>>>>
# lucky 准备开吃
# lucky 吃了 狗不理包子
# lucky 吃了 饺子
# lucky 吃了 肉夹馍
yield
1.帮你提供了一种自定义生成器方式(自定义生成器必须要有yield)
2.会帮你将函数的运行状态暂停住
3.可以返回值

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

生成器表达式

列表生成式

res = [i for i in range(1,10) if i != 4]
print(res)
>>>
[1, 2, 3, 5, 6, 7, 8, 9]

生成器表达式

res = (i for i in range(1,10) if i != 4)  # 注意这里括号是()不是[]
print(res)  # <generator object <genexpr> at 0x101396eb8> # 老母鸡

  # 生成器不会主动执行任何一行代码

  # 必须通过__next__触发代码的运行

res = (i for i in range(1,10) if i != 4)  # 注意这里括号是()不是[]
print(res)  # <generator object <genexpr> at 0x101396eb8> # 老母鸡

print(res.__next__())  # 1
print(res.__next__())  # 2
print(res.__next__())  # 3
print(res.__next__())  # 5

 

 

  面试题1

def add(n,i):
    return n+i
def test():
    for i in range(4):
        yield i
g=test()  # 函数变成生成器

for n in [1,10]:  # 遍历集合中1和10两个元素,执行两次
    g=(add(n,i) for i in g)  # 生成器表达式,只有调用__next__方法才能执行代码
    # 第一次for循环g=(add(n,i) for i in test())
            # g 生成器,不执行 
    # 第二次for循环g=(add(n,i) for i in (add(n,i) for i in test()))
            # g 生成器,不执行
print(n)
res=list(g)
    # list的使用原理是for循环,for循环会调用__next__迭代取值,会触发生成器执行代码
    # 执行第一次for循环 g=(add(n,i) for i in test())
    # 循环一次后g中的值[10,11,12,13],g是一个生成器
    # 执行第二次for循环g=(add(n,i) for i in g)
    # 循环两次后g中的值[20,21,22,23],g是一个生成器
#A. res=[10,11,12,13]
#B. res=[11,12,13,14]
#C. res=[20,21,22,23]  答案
#D. res=[21,22,23,24]

 

  面试题2

def multipliers():

    return [lambda x : i*x for i in range(4)]
    # 修改成这样就能输出想要的: return [lambda x, i=i: i*x for i in range(4)]
    # 0, 1, 2, 3
    # [func(x): return 0*x, /
        func(x): return 1*x, /
        func(x): return 2*x, /
        func(x): return 3*x, ]

print([m(2) for m in multipliers()])  

# [func(x): return 0*2, /
func(x): return 1*2, /
func(x): return 2*2, /
func(x): return 3*2, ]
#   理想结果是   [0, 2, 4, 6]
# 但现实输出的是  [6, 6, 6, 6]

# 闭包函数的延迟绑定
# 在内层函数执行时才会绑定变量i

# 可以改成这样的形式,就容易理解了
def multipliers2(): list1 = [] for i in range(4): def func(x): #应该这么修改:def func(x, i=i): return x * i        list1.append(func) return list1 print([m(2) for m in multipliers2()]) # 这里会给multipliers传参为2并执行
# 修改前:[6, 6, 6, 6] # 修改后:[0, 2, 4, 6] 

 常用的内置方法part2

################abs#######################
print
(abs(-13.24)) # abs 求绝对值 ###################all和any########## l = [1,1,0] print(all(l)) # all 存在一个为False就返回False print(any(l)) # any 任意一位为True就返回True ##############local和globals############# def index(): username = '我是局部名称空间里面的username' print(locals()) # locals 返回当前所在区域存储的所有的名字 print(globals()) # globals 无论在哪 返回的都是全局名称空间 index() #################进制转换################### print(bin(10)) # bin 十进制转2进制 print(oct(10)) # oct 十进制转8进制 print(hex(10)) # hex 十进制转16进制 print(int('0b1010',2)) # 其他进制转十进制 ##############bool################### print(bool(1)) print(bool(0)) # bool判断真假 ######################encode################ s = 'hello你好' print(s.encode('utf-8')) # encode 编码 print(bytes(s,encoding='utf-8')) # 字节流 ##################callable##################### def index(): pass print(callable(index)) # callable 可调用的(可以加括号执行相应功能的) ###########chr和ord################### print(chr(65)) # chr 将数字转换成ascii码表对应的字符 print(ord('A')) # ord 将字符按照ascii表转成对应的数字 ###############dir########## print(dir(range(12))) # dir 获取当前对象名称空间里面的名字 ##################divmod################## print(divmod(101,10)) total_num,more = divmod(900,11) if more: total_num += 1 print('总页数:',total_num)# divmod 分页器 ###############enumerate#################### l = ['a','b','c','d'] for i,j in enumerate(l,1):# enumerate 枚举 print(i,j) ####################eval################ # eval exec 执行字符串表达式 s1 = """ print(1 + 2) for i in range(10): print(i) """ # eval(s1) exec(s1) name = 'jason' s2 = """ name """ print(eval(s2)) # eval不支持逻辑代码,只支持一些简单的python代码 #######################help############################# def login(): """ 一起嗨皮 :return: """ print(help(login)) # help 查看函数注释 ######################isinstance############################ print(isinstance("123",list)) # isinstance 判断对象是否属于某个数据类型 print(pow(2,3)) # 返回 x的y次方。 print(round(3.4)) # round 四舍五入,取整

面向对象编程简介

  面向过程编程:就类似于设计一条流水线
    好处:
    将复杂的问题流程化 从而简单化
    坏处:
    可扩展性较差 一旦需要修改 整体都会受到影响

posted @ 2019-07-15 20:07  Bitten  阅读(241)  评论(0编辑  收藏  举报