内置函数
filter(函数,可迭代对象)
map(函数,可迭代对象)
sorted(值,key=指定排序规则,reverse=True)
reversed(值)zip(值,值,值)
reduce(函数,可迭代对象)
zip(值,值,值)
迭代器
-
可迭代对象
- 能够for循环的就是一个可迭代对象
- 具有
__iter__
方法的就是可迭代对象
-
迭代器
在上次停留的位置继续去做一些事情
lst = ["a","b","c","d","e","f"] lst1 = lst.__iter__() #创建了一个迭代器 print(id(lst)) #查看lst的内存地址 print(id(lst[0])) #查看lst[0]的内存地址 print(lst1) #迭代器的内存地址 print(lst1.__next__) print(list(lst1)) # 将迭代器转换成了一个列表 结果: 264140179464 264139523944 <list_iterator object at 0x0000003D7FFB0198> 'a' ['a', 'b', 'c', 'd', 'e', 'f']
根据以上代码,迭代器我判断就是开辟新的内存地址,然后指向了原来的那个列表
for循环的本质
lst = [1,2,3,1,4,5,6,7] #新建一个列表
lst1 = lst.__iter__() #创建一个迭代器lst1
while True:
try:
print(lst1.__next__()) #输出迭代的值
except:StopIteration #如果遇到这个错误就结束
迭代器在次迭代还是原来那个迭代器
lst = [1,2,3,1,4,5,6,7] #新建一个列表
lst1 = lst.__iter__() #创建一个迭代器lst1
print(lst1)
print(lst1.__iter__()) #迭代器在次迭代还是原来的值
结果:
<list_iterator object at 0x00000083C34200B8>
<list_iterator object at 0x00000083C34200B8>
具有__iter__()
方法的就是一个可迭代对象
迭代器具有__iter__()
和__next__()
迭代器一定是一个可迭代对象,但是可迭代对象不一定是一个迭代器
可迭代对象的优点
- 方便查找
- 使用灵活
可迭代对象的缺点
占内存
迭代器的优点
节省内存–惰性机制
迭代器的缺点
- 查找不方便
- 使用不灵活
- 一次性的
- 不能后退
迭代器是Python自己提供的一种节省空间的方式
生成器
生成器的本质就是迭代器
迭代器是Python
自带的
生成器是程序猿自己写的
生成器如何编写
-
基于函数编写生成器
def func(): yield 1 g = func() print(g) print(g.__next__()) print(g.__iter__()) 结果: <generator object func at 0x000000B9116A2E60> 1 <generator object func at 0x000000F585EF2E60>
yield相当于return
一个yield对应一个next
怎么区分迭代器和生成器
-
通过内存地址来分辨
迭代器:<list_iterator object at 0x00000091FE4402B0>
生成器:<generator object func at 0x00000091FE402E60>
后面的地址是变化的,只是拿了最前面的做判断
-
通过send()方法来分辨
迭代器没有send()方法
生成器有send()方法
生成器节省空间
时间换空间 :
空间换时间 :
当文件或数据量较大时使用生成器
yield from和yield的区别:
yield后边的内容整体返回
yield from将可迭代对象中的元素逐个返回
推荐使用iter()和next()
-
-
基于推导式
推导式
列表推导
普通模式:
print([i for i in range(10)])
结果:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
筛选模式:
#[加工后变量 for循环 加工条件]
print([i for i in range(10) if i > 5])
print([i for i in range(10) if i % 2 == 1])
print([i%2 for i in range(10)])
字典推导
普通模式
print({i:i+1 for i in range(10)})
结果:
{0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9, 9: 10}
筛选模式
print({i:i+1 for i in range(10) if i == 3})
结果:
{3: 4}
集合推导
普通模式
print({i for i in range(10)})
结果:
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
筛选模式
print({i for i in range(10) if i > 5 and i > 7})
结果:
{6}
生成器推导
普通模式
g = (i for i in range(10))
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
结果:
0
1
2
3
4
筛选模式
g = (i for i in range(10) if i - 6 == 2)
print(next(g))
结果:
8
面试必考
第一题:
lst = [lambda x:i for i in range(5)] #将推导式的地址赋值给Lst #次行是先执行完for循环,然后去下面接着执行
print(lst)
print([em(1) for em in lst]) # [4,4,4,4,4] 执行一次循环,使用一次em,去调用一次50行,不断调用
第二题:
lst = []
for i in range(5):
lst.append(lambda x:i) #添加进去的是每个元素的地址 五次执行完毕,匿名函数的返回值最后是i,由于for循环完的时候I的值是4
print(lst) #每个元素的地址
第三题:
new_lst = []
for em in lst:
new_lst.append(em(1)) # 调用em()函数就是调用lst里面的地址对应的值,但是em()的返回值是4,所以不论执行多少次,他始终添加的值为4
print(new_lst) #[4, 4, 4, 4, 4]
第四题:
lst = [lambda :i+1 for i in range(3)] #lst拿了匿名函数的地址0 1 2的内存地址,返回的值为i+1 i为2
for i in lst:
print(i)
print({em():em()+1 for em in lst}) #首先此循环会执行三次,因为Lst里面有三个内存地址,但是i的值此时为2,所以返回的就是3:4。因为是字典,字典会存在key值得之后自动赋值新值,所以最后的4是最后一次循环的值
第五题:
g = (lambda x:x+i for i in range(3)) # 惰性 生成器推导需要每次用的时候才会执行,不执行自己永远不会动
print([em(2) for em in g]) # [4,4,4] #此题我故意给的错误答案,若要看正确的,请看下面的详细分析。
"""
第一次:
[em(2) for em in g]
使用到g,g拿到的是推到器推导到的内存地址,所以去了推到器推导进行分析
此时第一次i的值为0,传递的实参为2,所以返回值x+i的值为2
第二次:
[em(2) for em in g]
使用到g,g拿到的是推到器推导到的内存地址,所以去了推到器推导进行分析
此时第二次i的值为1,传递的实参为2,所以返回值x+i的值为3
第三次:
[em(2) for em in g]
使用到g,g拿到的是推到器推导到的内存地址,所以去了推到器推导进行分析
此时第三次i的值为1,传递的实参为2,所以返回值x+i的值为4
"""
def func():
for i in range(3):
yield lambda x:x+i #yield保存上一次取得地址,下次来了直接Next
g = func() #g拿到的是func的内存地址
print(g)
lst = []
for em in g:
lst.append(em(2)) #2 3 4
print(lst)
g = [lambda x:x+i for i in range(3)] #g拿到的是lambda的三个内存地址,此时I的值为2
print([em(2) for em in g]) # [4,4,4]
g = (lambda x:x+i for i in range(10)) # 惰性
print([next(g)(2) for em in range(5)]) #[2, 3, 4, 5, 6]