使用dir()我们可以知道这个数据类型的内置函数有什么方法:
print(dir(int)) print(dir(bool)) print(dir([])) print(dir({})) print(dir(set))
1.迭代器
iterable:可迭代的
迭代就是将数据能够一个一个按顺序取出来
s = 'abc' print('__iter__' in dir(s)) #True li = [1,2] print('__iter__' in dir(li)) #True b = False print('__iter__' in dir(b)) #False i = 123 print('__iter__' in dir(i)) #False dic = {} print('__iter__' in dir(dic)) #True set1 = set() print('__iter__' in dir(set1)) #True
上面数据类型返回为真说明它是可以迭代的,反之是不可迭代的
可迭代协议:
就是内部要有一个__iter__()来满足要求
当一个具有可迭代的数据执行__iter__()它将返回一个迭代器的内存地址
print('abc'.__iter__()) #
这里的iterator的意思是迭代器
迭代器协议:
现在有一个列表我们来看看它本身和在执行了__iter__()之后的方法有什么不同:
li = [1,2,3,'a'] print(dir(li)) #['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__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'] print(dir(li.__iter__())) #['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__'] '''求两者的差集''' print(set(dir(li.__iter__())) - set(dir(li))) #{'__length_hint__', '__setstate__', '__next__'}
__length_hint__()的作用是求元素的长度
__setstate__()的作用是指定索引值从哪里开始迭代
__next__()的作用可以让值一个一个的取出
在之前用到的for循环我们就是用__next__()这种方法进行取值,现在我们可以模拟for来写一个函数:
li = [1,2,3,'a'] def getItem(li): iterator = li.__iter__() while True: print(iterator.__next__()) getItem(li) # 1 # 2 # 3 # a #StopIteration 如果找不到元素就会报错
如何处理掉这个异常?
li = [1,2,3,'a'] def getItem(li): iterator = li.__iter__() while True: try: print(iterator.__next__()) except StopIteration: break getItem(li) # 1 # 2 # 3 # a
迭代器遵循迭代器协议:必须要有__iter__()和__next__()
迭代器的好处:
a.从容器类型中一个一个的取值,会把所有的值都取到
b.节省内存的空间
迭代器并不会在内存中占用一大块内存,而是随着循环每次生成一个,每一次使用__next__()来给值
range():
print(range(10000000)) #range(0, 10000000)
实际上range()在调用的时候并没有真正生成这么多的值,如果真的生成的话那么内存可能会溢出
print('__iter__' in dir(range(10))) #True print('__next__' in dir(range(10))) #False from collections import Iterable from collections import Iterator print(isinstance(range(10),Iterable)) #True 是一个可迭代对象 print(isinstance(range(10),Iterator)) #False 在执行后得到的结果并不是一个迭代器
迭代器总结:
1.可以被for循环的都是可迭代的
2.可迭代的内部都有__iter__()方法
3.只要是迭代器一定可以迭代
4.可迭代的变量.__iter__()方法可以得到一个迭代器
5.迭代器中的__next__()方法可以一个一个的获取值
6.for循环实际上就是在使用迭代器
2.生成器
生成器函数
本质上就是我们自己写的函数,只要含有yield关键字的函数就是生成器函数,yield不能和return共用且需要写在函数内部
def generator(): #生成器函数 print('a') yield 5 ret = generator() #ret是一个生成器 print(ret) #
生成器函数每次执行的时候会得到一个生成器作为返回值
如果要返回函数值:
def generator(): #生成器函数 print('a') yield 5 print('b') yield 4 g = generator() #g是一个生成器 print(g) #ret = g.__next__() print(ret) #a #5 print('---------') ret = g.__next__() print(ret) #b #4
执行顺序:
使用for循环遍历生成器:
def generator(): #生成器函数 print('a') yield 5 print('b') yield 4 print('c') yield 6 g = generator() #g是一个生成器 for i in g: print(i) # a # 5 # b # 4 # c # 6
send():
send()和__next__()都有获取下一个值的功能,但send()在获取下一个值的时候可以给上一个yield的位置传递一个数据,让用户进行接收
send()在使用时需注意在第一次使用生成器时需用__next__()获取下一个值并且最后一个yield不能接受外部的值
def generator(): #生成器函数 print('a') #1 content = yield 1 #2 print(content,'= 1') #3 print('b') #4 content2 = yield 2 #5 print(content2,'= 2')#6 yield 3 #7 g = generator() #生成器g ret = g.__next__() #执行1和2的右边语句 print(ret) #返回yield的值1 ret = g.send('a') #传递给yield一个值'a'让content接收,然后执行3,4语句 print(ret) #返回yield的值2 ret = g.send('b') #传递给yield一个值'b'让content2接收,然后执行6语句 yield再返回一个值3 print(ret) # a # 1 # a = 1 # b # 2 # b = 2 # 3
移动平均值例子:
def average(): #定义一个平均值的生成器函数 sum = 0 count = 0 avg = 0 while True: num = yield avg sum += num count += 1 avg = sum/count avg_g = average() #取得生成器 avg_g.__next__() avg1 = avg_g.send(int(input())) #10 avg1 = avg_g.send(int(input())) #20 print(avg1) #15
预激装饰器的生成器
def wrapper(func): def inner(*args,**kwargs): g = func(*args,**kwargs) g.__next__() return g return inner @wrapper #wrapper = wrapper(func) def avgerage(): sum = 0 count = 0 avg = 0 while True: num = yield avg sum += num count += 1 avg = sum/count avg_g = avgerage() print(avg_g.send(10)) print(avg_g.send(20)) print(avg_g.send(30))
yiled form
如何将字符串一个一个的去返回?
一般方法:
def generator(): a = 'abcde' b = 'efdgh' for i in a: yield i for i in b: yield i g = generator() for i in g: print(i)
用yiled form:
def generator(): a = 'abcde' b = 'efdgh' yield from a yield from b g = generator() for i in g: print(i) # a # b # c # d # e # e # f # d # g # h
监听文件例子:
def tail(filename): f = open(filename,encoding='utf-8') while True: line = f.readline() if line.strip(): yield line.strip() g = tail('tail') for i in g: if 'python' in i: print('******',i) # ****** python # ****** asd python
3.推导式
优点:基本不消耗内存
语法:以列表推导式为例
(1)遍历元素进行挨个处理:[每一个元素或者是和元素相关的操作 for 元素 in 可迭代数据类型]
(2)筛选功能:[满足条件的元素的相关操作 for 元素 in 可迭代数据类型 if 元素相关的条件]
1.列表推导式
li = [i*i for i in range(5)] print(li) #[0, 1, 4, 9, 16]
找到50以内能被5整除的数:
li = [i for i in range(50) if i % 5 == 0] print(li) #[0, 5, 10, 15, 20, 25, 30, 35, 40, 45]
找到两个列表中都含有一个'x'的元素
names = [['xasd','12sa','xa'],['564','566','x233']] li = [name for lst in names for name in lst if name.count('x') == 1] print(li) #['xasd', 'xa', 'x233']
2.生成器推导式
g = (i*2 for i in [1,5,6,7]) print(g) #at 0x006E4F00> for i in g: print(i) # 2 # 10 # 12 # 14
3.字典推导式
将字典的键值互换
dict = {'key1' : 'value1','key2' : 'value2','key3' : 'value3'} print(dict) #{'key3': 'value3', 'key2': 'value2', 'key1': 'value1'} new_dict = {dict[i] : i for i in dict} print(new_dict) #{'value2': 'key2', 'value1': 'key1', 'value3': 'key3'}
合并大小写对应的value值,再将键都统一成小写
dict = {'a' : 1,'b' : 2,'A' : 3,'c' : 4} new_dict = {k.lower() : dict.get(k.lower(),0) + dict.get(k.upper(),0) for k in dict} #如果没有找到这个值就返回0 print(new_dict) #{'c': 4, 'b': 2, 'a': 4}
4.集合推导式
具有去重复的功能
set = {x**2 for x in range(-5,6)} print(set) #{0, 1, 4, 9, 16, 25}
4.相关面试题
例一:
def generator(): for i in range(4): yield i g = generator() g1 = (i for i in g) g2 = (i for i in g1) print(list(g1)) #[0, 1, 2, 3] print(list(g2)) #[] 因为g2是往g1去拿值,但是g1已经从g用for循环遍历完了值,所以g2是一个空的列表
例二:
def add(n,i): return n+i def test(): for i in range(4): #0 1 2 3 yield i g = test() for n in [1,10]: g = (add(n,i) for i in g) ''' n = 1 g = (add(n,i) for i in g) 这里的代码因为跑过了 就不执行 n = 10 g = (add(n,i) for i in (add(n,i) for i in test())) 所以 g = (add(10,i) for i in (add(10,i) for i in [0,1,2,3])) g = (add(10,i) for i in (10,11,12,13) g = (20,21,22,23) ''' print(list(g)) #[20, 21, 22, 23] 当没有调用的时候上面的代码都不会执行