迭代器

迭代器

可迭代对象:具有__iter__方法的都可叫做可迭代对象

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

n = 1
f = 1.1
s = 'hello'
l = [1,2,45]
t= (1,3,56)
d = {'name':'jaosn'}
s1 = {1,3,4,5}
f1 = open('xxx.txt','w',encoding='utf-8')

res = s.__iter__()
print(s.__len__())  # 5
res1 = l.__iter__()
res2 = f1.__iter__()
print(res1,res1,res2)
print(f1)  # 文件本身就是迭代器对象

<list_iterator object at 0x03B87D30> <list_iterator object at 0x03B87D30> <_io.TextIOWrapper name='xxx.txt' mode='w' encoding='utf-8'>

<_io.TextIOWrapper name='xxx.txt' mode='w' encoding='utf-8'>

f1 = open('xxx.txt','r',encoding='utf-8')
iter_f = f1.__iter__()
print(iter_f is f1)  # True

什么是迭代器:

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

​ 迭代器:迭代取值的工具

为什么要用:

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

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

迭代器对象:

​ 1、内置有__iter__方法和__next__方法的可迭代对象

​ ps:迭代器对象一定是可迭代对象,可迭代对象不一定是迭代器对象。

迭代器对象取值必须使用__next__方法。

l = [1,2,3,4]
iter_l = l.__iter__()

print(iter_l.__next__())
print(iter_l.__next__())
print(iter_l.__next__())
print(iter_l.__next__())
print(iter_l.__next__())  # 如果取完了,会直接报错

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

print(f1 is f1.__iter__().__iter__().__iter__().__iter__())  # True

问题:

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

因为利用for循环取值的时候,会自动使用__iter__方法将可迭代对象转化为迭代器对象,进而取值。而如果将文件内置的__iter__方法取消了,就不能利用for循环取出列表的每一行内容。

迭代器取值的特点:

​ 1、只能往后依次取,不能后退。取完之后,会直接报错。

# 异常处理
d = {'name':'jsson','password':'123','hobby':'泡面'}
iter_d = d.__iter__()
while True:
	try:
    	print(iter_d.__next__())
    except StopIteration:
#		print('取完了')
		break    

for 循环取值的本质

for循环里面的in关键字后面跟的是一个可迭代对象。

for循环内部的本质:

​ 1.将in后面的对象调用__iter__方法转化成迭代器对象

​ 2.调用__iter__迭代取值

​ 3.内部有异常捕获StopIteration,当__next__报这个错,循环自动结束

可迭代对象:内置有__iter__方法的对象

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

迭代取值的优点:

​ 1.不依赖于索引取值,

​ 2.内存中永远只占一份空间,不会导致内存溢出。

缺点:

​ 1.不能够获取指定的元素

​ 2.取完之后会报StopIteration错

生成器

生成器:用户自定义的迭代器,本质就是迭代器,里面含有yield关键字。

函数内如果有yield关键字,那么加括号执行函数的时候并不会触发函数体代码的运行。

def func():
    print('first')
    yield 666
    print('second')
    yield 777
    print('third')
    yield 888
    print('forth')
    yield 
    yield
g = func()  # 生成器初始化:将函数变成迭代器    
print(g)  # <generator object func at 0x00857D30>
print(g.__next__())  # 666
print(g.__next__())  # 777
print(g.__next__())  # 888
print(g.__next__())  # None
print(g.__next__())  # None

yield后面跟的值就是调用迭代器__next__方法你能得到的值

yield既可以返回一个值也可以返回多个值,并且多个值也是按照元组的形式返回

for i in range(1,10):
    print(i)
# 使用生成器生成range(1,10)
def my_range(start,end,step=1):
    while start < end:
        yield start
        start += step

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

yield关键字

yield的英文单词意思是生产,在函数中但凡出现yield关键字,再调用函数,就不会继续执行函数体代码,而是会返回一个值。yield支持外界为其传参

def dog(name):
    print(f'{name}准备开吃了')
    while True:
        food = yield
        print(f'{name}吃了{food}')

g = dog('wangcai')
g.__next__()  # wangcai准备开吃了 必须先将代码运行至yield,才能为其传值。
g.send('狗不理包子')  # wangcai吃了狗不理包子  # 给yield左边的变量传值,自动触发__next方法
g.send('饺子')  # wangcai吃了饺子

当函数内有yield关键字的时候,调用该函数不会执行函数体代码,而是将函数变成了生成器。

yield的特点:

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

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

​ 3.可以返回值

与return之间的异同点:

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

​ 不同点:yield可以返回多次值,而return只能返回一次函数立即结束。

​ yield还可以接收外部传入的值。

生成器

res = (i for i in range(1,10) if i != 4)
print(res)
res = (i for i in range(1,10) if i != 4)
print(res)

print(res.__next__())
print(res.__next__())
print(res.__next__())
print(res.__next__())
print(res.__next__())

生成器不会主动执行任何一行代码。必须通过__next__触发代码的运行

def add(n,i):
    return n+i
def test():
    for i in range(4):
        yield i
g=test()

for n in [1,10]:
    g=(add(n,i) for i in g)
    # 第一次for循环g=(add(n,i) for i in test())

    # 第二次for循环g=(add(n,i) for i in (add(n,i) for i in test()))
print(n)
res=list(g)

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

res=[20,21,22,23]

面试题一

def demo():
    for i in range(4):
        yield i
g = demo()
g1 = (i for i in g)
g2 = (i for i in g1)

print(list(g1))  # [0,1,2,3]
print(list(g2))  # []

# g是第一个生成器,g1取得值来自g,g2的值来自于g1,归根到底还是g,list是可迭代对象,将g1赋予list方法,其实是从g中取值,而且取完了,g2自然就是空值了。

def demo():
    for i in range(4):
        yield i
g = demo()
g1 = (i for i in g)
g2 = (i for i in g1)
print(next(g))  # 0
print(next(g1))  # 1
print(next(g2))  # 2

面试题二

def add(n,i):
    return n+i
def text():
    for i in range(4):
        yield i
g = text()
for n in [1,10]:
    g = (add(n,i) for i in g)
    
print(list(g))
# 其结果为[20,21,22,23]
# 如果将for循环下面的表达式换成g = [add(n,i) for i in g] 结果为[11,12,13,14]
# 如果将for循环的表达式换成for n in [1,2,10],或者for n in [1,11,10],其结果为[30,31,32,33]
# 如果是for n in [1,3,6,7,10,11,100,10],其结果为[80,81,82,83]

面试二总结:生成器的特点是惰性机制,也就是说你找它要,它才会给你值

​ 在上一题的for循环中,不管你循环了多少次,生成器的取值都是[]中的最后一个值

​ g在取值之前,是一个内存地址,for循环一直改变着g的内存地址,最后是n=10的内存地址

​ for循环一次,g中的值就加一次,每次加的都是n=10的内存地址的值

​ for循环第一次:10+0 10+1 10+2 10+3

​ for循环第二次:10+10+0 10+10+1 10+10+2 10+10+3

​ 如果有第三次:10*3+0 10*3+1 10*3+2 10*3+3

​ .........

常用内置方法

abs()绝对值

print(abs(-13.5))  # 13.5

all(),any()

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

bin()/oct()/hex() 二进制/八进制/16进制

print(bin(17))
print(oct(17))
print(hex(17))

0b10001
0o21
0x11

bool()布尔值

print(bool(1))  # True
print(bool(0))  # False

bytes()字节二进制

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

callable()可调用的(可以加括号执行相应功能的)

l = [1,2,3]
def index():
    pss
print(callable(l))  # False
print(callable(index))  # True

chr(),ord()

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

dir()获取当前对象名称空间里面的名字

l = [1,2,4]
print(dir(l))

['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']


divmod()分页器

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

enumerate() 枚举

l = ['a','b']
for i,j in enumerate(l,1):  # 带有所以索引的迭代
    print(i,j)
    
1 a
2 b    

eval(),exec()

s = """
print('hello baby~')
x = 1
y = 2
print(x+y)
"""
print(eval(s))  # eval不支持逻辑代码,支持支一些简单的python代码

s = """
print('hello baby~')
x = 1
y = 2
print(x+y)
"""
exec(s)  # 支持逻辑代码

hello baby~
3

isinstance()

# isinstance 判断对象是否属于某个数据类型
n = 1
print(type(n))
print(isinstance(n,list))  # 判断对象是否属于某个数据类型


pow(),计算第一个数的几次方

print(pow(2,3))  # 8

round()四舍五入

print(round(3,4))  # 3

global()/locals()

def index():
    username = '我是局部名称空间里面的username'
    print(locals())  # 当前语句在哪个位置,就会返回哪个位置所存储的所有的名字
    print(globals)  # 无论在哪,查看的都是全局名称空间

面向过程编程

面向过程编程:就类似于设计一条流水线

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

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

面试题3

请写出一下代码的执行结果并解释。

def multipliers():
    return [lambda x:i*x for i in range(4)]
print([m(2) for in multipliers()])

# 解析:
# [lambda x:i*x for i in range(4)]

[func(x):return 0*x,func(x):return 1*x,func(x):return 2*x,func(x):return 3*x]

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

def multipliers2():
	list1 = []
	for i in range(4):  # 0,1,2,3
        def func(x):
            return x * i
        list1.append(func)
    return list1
print([m(2) for m in multipliers2()])
m = lambda x:i*x
m = lambda x:i*x
m = lambda x:i*x
# 每次都执行一次m(2),也就是每次都调用了一下匿名函数,注意:此时此刻 匿名函数才真正被调用了,它会引用外层空间的变量i,那么此时i的值是多少呢?
# 因为for i in range(3)这个循环已经执行完毕,i的值等于3,所以每次当执行m(2)时,i的值都等于3。所以每次调用m(2)的结果都是6.
[6,6,6,6]

改进

def multipliers():
	
    return [lambda x, i=i: i*x for i in range(4)]
# return [lambda x,i=0: i*x, lambda x,i=1: i*x, lambda x,i=2: i*x, lambda x,i=3:i*x i=3]
# x的引用是2 所以output的结果就是:[0,2,4,6]


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

def multipliers2():
    list1 = []
    for i in range(4):

        def func(x, i=i):

            return x * i

        list1.append(func)

    return list1

print([m(2) for m in multipliers2()])  # [0, 2, 4, 6]

添加了一个i = i后,就给匿名函数,添加了一个默认参数,而python函数中的默认参数,是在python解释器遇到def(i=i)或lambda关键字时,就必须初始化默认参数。

此时for i in range(4),每循环一次,匿名函数的默认参数i,就需要找一次i的引用。i=0时,第一个匿名函数的默认参数值就是0,i=1时,第二个匿名函数的默认参数值就是1,以此类推。

需求:读取一个文件并返回每行数据的长度

with open('text.txt','w',encoding='utf-8') as f:
    for line in range(1000):
        f.write(f'www{line}aaa'*(line + 1) + '\n')

# 列表推导式:处理数据量大的文件会导致内存溢出。
res = [len(line) for line in open('text.txt','r',encoding='utf-8')]
# print(res)

# 生成器表达式:处理数据量大的文件推荐使用
res2 = (len(line) for line in open('text.txt','r',encoding='utf-8'))
print(res2)  # <generator object <genexpr> at 0x000002B3748FD0A0>
print(next(res2))
print(next(res2))

posted @ 2019-07-15 22:29  最后的别离  阅读(123)  评论(0编辑  收藏  举报