迭代器
迭代器
可迭代对象:具有__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))