迭代器
"""
迭代器:
能被next()函数调用并不断返回下一个值的对象称为迭代器(Iterator 迭代器是对象)
概念:
迭代器指的是迭代取值的工具,迭代是一个重复的过程,每次重复都是基于上一次的结果而继续的,
单纯的重复并不是迭代
特征:
并不依赖索引,而通过next指针(内存地址寻址)迭代所有数据,一次只取一个值,
而不是一股脑的把所有数据放进内存.大大节省空间,
"""
一.可迭代对象
内置函数: dir
获取当前对象的内置成员
setvar = {"王同培","马春配","赵万里","赵沈阳"}
# 获取当前对象的内置成员
lst = dir(setvar)
print(lst)
# 判断是否是可迭代对象
res = "__iter__" in lst
print(res) # True
for i in setvar:
print(i)
二.迭代器
"""
for循环之所以可以遍历所有的数据,是因为底层使用了迭代器,通过地址寻址的方式,一个一个的找数据;
可迭代对象 -> 迭代器 实际上就是从不能够被next直接调用 -> 可以被next指针直接调用的过程
如果是可迭代对象 -> 不一定是迭代器
如果是迭代器 -> 一定是可迭代对象
"""
1.如何创建一个迭代器
setvar = {"王同培","马春配","赵万里","赵沈阳"}
it = iter(setvar)
print(it) # <set_iterator object at 0x00>
2.如何判断一个迭代器
print(dir(it))
res = "__iter__" in dir(it) and "__next__" in dir(it)
print(res) # True
3.如何调用一个迭代器
"""next是单向不可逆的过程,一条路走到黑"""
res = next(it)
print(res) # 赵沈阳 (无序的)
res = next(it)
print(res)
res = next(it)
print(res)
res = next(it)
print(res)
# res = next(it) # 报错 StopIteration
# print(res)
4.重置迭代器
it = iter(setvar) # 需要重新获取迭代器
print( it.__next__() ) # 可以使用迭代器的魔法方法
print( it.__next__() )
print( it.__next__() )
print( it.__next__() )
5.调用迭代器的其他方法
# 1 for调用
it = iter(setvar)
for i in it:
print(i)
# 2 for + next
it = iter(setvar)
for i in range(2):
print( next(it) )
print( next(it) ) # 第三个元素
print( next(it) )
# print( next(it) ) # error 超出了寻址范围
6.判断 迭代器Iterator
/可迭代对象Iterable
的其他方法
# 从...模块 引入...内容
from collections import Iterator, Iterable
"""Iterator 迭代器 Iterable 可迭代的对象"""
res = isinstance(it,Iterator)
print(res)
res = isinstance(it,Iterable)
print(res)
# 迭代器一定是可迭代对象;可迭代对象不一定是迭代器
7. range()
返回值是迭代器么?
print(isinstance(range(10),Iterator)) # False
print(isinstance(range(10),Iterable)) # True
# 变成迭代器
it = range(10).__iter__()
print(isinstance(it,Iterator)) # True
print(isinstance(it,Iterable)) # True
# 调用it
# next
res = next(it)
print(res)
res = next(it)
print(res)
# for
for i in it:
print(i)
高阶函数
高阶函数 : 能够把函数当成参数传递的就是高阶函数 (map ,filter ,reduce , sorted)
1. map
"""
map(func,iterable)
功能: 处理数据
把iterable中的数据一个一个拿出来,扔到func做处理,通过调用迭代器来获取返回值
参数:
func : 函数(内置函数,自定义函数)
iterable : 可迭代性对象 (容器类型数据,range对象,迭代器)
返回值:
迭代器
"""
# (1) 把列表中的元素都变成整型
lst = ["1","2","3","4"]
lst_new = []
for i in lst:
lst_new.append(int(i))
print(lst_new)
# 用map改写
from collections import Iterator,Iterable
it = map(int,lst) # map 返回迭代器
print(isinstance(it,Iterator))
"""
代码解析:
第一次调用迭代器
先把列表中的第一个元素"1"拿出来扔到int中做强转,变成整型1返回出来
第二次调用迭代器
先把列表中的第一个元素"2"拿出来扔到int中做强转,变成整型2返回出来
第三次调用迭代器
先把列表中的第一个元素"3"拿出来扔到int中做强转,变成整型3返回出来
第四次调用迭代器
先把列表中的第一个元素"4"拿出来扔到int中做强转,变成整型4返回出来
"""
# 1.调用迭代器 next
print(next(it))
print(next(it))
print(next(it))
print(next(it))
# print(next(it)) # error
# 2.调用迭代器 for
print("<======>")
it = map(int,lst)
for i in it:
print(i)
# 3.调用迭代器 for + next
print("<======>")
it = map(int,lst)
for i in range(3):
print(next(it))
# 4.强转迭代器 => 列表 (常用)
it = map(int,lst)
print(list(it))
# (2) [1,2,3,4] => [2,8,24,64]
# print(1 * 2 ** 1) <=> 1 << 1
# print(2 * 2 ** 2) <=> 2 << 2
lst = [1,2,3,4]
lst_new = []
for i in lst:
lst_new.append(i << i)
print(lst_new)
# map改写
def func(n):
print(1111)
return n << n
it = map(func,lst) # 只调用map函数时,func函数不会被调用
print(list(it))
"""
强调:
只有在调用迭代器的时候,才会真正触发map函数中的所有内容;不调用不触发;
强转迭代器时,把可以调用的所有数据都放到列表中
注意点: 形参和返回值必须写;
"""
# (3) 已知一个字典,给你一个列表["a","b","c"] => [97,98,99]
# 字典的键值翻转操作
dic = {97:"a",98:"b",99:"c"}
dic_new = {}
for k,v in dic.items():
# print(k,v) # 97 a | 98 b | 99 c
dic_new[v] = k # dic_new["a"] = 97
print(dic_new)
lst = ["a","b","c"]
lst_new = []
for i in lst:
lst_new.append(dic_new[i])
print(lst_new)
# map改写
print("<========================>")
lst = ["a","b","c"]
lst = ["c","b","a"]
lst = ("c","b","a")
# func 实现字典的翻转,通过给与a,b,c三个键,得到对应的ascii码,通过list强转得到列表
def func(n):
print(n)
dic = {97:"a",98:"b",99:"c"}
dic_new = {}
for k,v in dic.items():
dic_new[v] = k
print(dic_new) # {'a': 97, 'b': 98, 'c': 99}
return dic_new[n]
it = map(func,lst)
print(list(it))
2. reduce
"""
reduce(func,iterable)
功能: 计算数据
把iterable中的前两个数据扔到func函数中做计算,把计算的结果和iterable中第三个值在继续扔到func中做计算
以此类推 ...
最后返回计算的结果
参数:
func: 自定义函数
iterable : 可迭代对象 (容器类型数据 range对象 迭代器)
返回值:
计算的结果
"""
案例:
# (1) [7,7,5,8] => 7758
lst = [7,7,5,8]
# 方法一
strvar = ""
for i in lst:
strvar += str(i)
res = int(strvar)
print(res , type(res))
# 方法二
"""
7 * 10 + 7 = 77
77 * 10 + 5 = 775
775 * 10 + 8 = 7758
"""
# 1.先变成迭代器
it = iter(lst)
# 2.取出两个值
num1 = next(it)
num2 = next(it)
print(num1,num2)
# 做计算
total = num1 * 10 + num2
print(total) # 77
# 3.把计算的结果在和剩下的数据做计算
for num in it:
total = total * 10 + num
# 4.返回最后的结果
print(total , type(total))
# reduce改写
'''从...functools模块, 引入 .. reduce方法'''
from functools import reduce
lst = [7,7,5,8]
def func(x,y):
# print(x,y)
return x * 10 + y
res = reduce(func,lst)
print(res)
# 使用lambda 进行改造
print(reduce(lambda x,y: x*10 + y,lst))
# (2) "123" => 123 不使用int的情况下实现该操作;
strvar = "123"
def func(x,y):
return x * 10 + y # 字符串的拼接
# 把字符串"123" 处理成数字的123
def func2(n):
# dic = {"0":0,"1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9}
dic = {}
for i in range(10):
dic[str(i)] = i
return dic[n]
it = map(func2,strvar)
# res = reduce(func,it)
# print(res,type(res))
# 简写
print(reduce(lambda x,y: x*10 + y,it))
3. filter
"""
filter(func,iterable)
功能: 过滤数据
在自定义的函数中,
如果返回True, 该数据保留
如果返回False,该数据舍弃
参数:
func: 自定义函数
iterable : 可迭代对象 (容器类型数据 range对象 迭代器)
返回值:
迭代器
"""
# 1.只要列表中所有的偶数
lst = [1,2,34,5,65,6,56,7,56,756,7567,11]
lst_new = []
for i in lst:
if i % 2 == 0 :
lst_new.append(i)
print(lst_new)
# filter改写
def func(n):
if n % 2 == 0:
return True
else:
return False
it = filter(func,lst)
print(list(it))
# 使用lambda 改写
it = filter(lambda n :True if n % 2 == 0 else False , lst)
print(list(it))
print(list(filter(lambda n :True if n % 2 == 0 else False , lst)))
4. sorted
"""
sorted(iterable, *, key=函数, reverse=False)
功能:排序数据
参数:
iterable : 可迭代对象 (容器类型数据 range对象 迭代器)
key : 指定函数(自定义/内置)
reverse : 是否倒序
返回值:
列表
"""
# 1.从小到大
tup = (-90,89,78,3)
res = sorted(tup)
print(res,type(res)) # list
# 2.从大到小
res = sorted(tup,reverse = True)
print(res,type(res))
# 3.按照绝对值进行排序
tup = (-90,-100,1,2)
res = sorted(tup,key=abs)
print(res)
"""
1 => abs(1) => 1
2 => abs(2) => 2
-90 => abs(-90) => 90
-100 => abs(-100) => 100
"""
# 4.按照自定义函数进行排序
tup = (19,23,42,87)
"""
42 % 10 2 => 42
23 % 10 3 => 23
87 % 10 7 => 87
19 % 10 9 => 19
"""
def func(n):
print(n)
return n % 10
lst = sorted(tup,key = func) # 必须要用key指定
print(lst)
# 5.任意的容器类型数据都可以通过sorted排序
container = "abc"
container = [1,2,3]
container = (1,2,3)
container = {"你好","王一","你真帅"}
container = {"caixukun","xiaozhan","zhaoshenyang","wangyibo"}
container = {"ww":"英俊帅气","zxy":"猥琐抠脚","zwl":"斯文败类"} # 排的是字典的键
print(sorted(container))
"""
# 总结:
sorted (推荐使用sorted)
(1) 可以排序所有的容器类型数据
(2) 返回一个新的列表
sort
(1) 只能排序列表
(2) 基于原来的列表进行排序
"""
推导式(comprehensions
)
"""
推导式 : 通过一行循环判断遍历出一些列数据的方法叫做推导式
通过一行循环判断,遍历出一系列数据的方式是推导式
语法: val for val in Iterable (把想要的值写在 for的左侧)
里面是一行循环判断!根据套在推导式外层的符号判断具体是什么类型的推导式
推导式种类三种:
[val for val in Iterable] 列表推导式
{val for val in Iterable} 集合推导式
{a:b for a,b in iterable} 字典推导式
"""
1.推导式基本语法
lst = []
for i in range(1,51):
lst.append(i)
print(lst)
# 改写推导式
lst = [ i for i in range(1,51) ]
print(lst)
# 小练习
# 1.[1,2,3,4,5] => [2,4,6,8,10]
lst = [ i*2 for i in range(1,6) ]
print(lst)
2.带有判断条件的推导式
"""注意点:for后面紧跟的判断条件只能是 单项分支 ."""
"""[1,2,3,4,5,6,7,8,9,10] => [1,3,5,7,9 ... ]"""
lst = [1,2,3,4,5,6,7,8,9,10]
lst_new = []
for i in lst:
if i % 2 == 1:
lst_new.append(i)
print(lst_new)
# 改写推导式
lst = [ i for i in lst if i % 2 == 1 ]
print(lst)
3.多循环推导式
lst1 = ["孙杰龙","陈露","曹静怡"]
lst2 = ["王志国","邓鹏","合理"]
lst_new = []
for i in lst1:
for j in lst2:
lst_new.append(i+"♡♢"+j)
print(lst_new)
# 改写推导式
lst = [ i+"♡♢"+j for i in lst1 for j in lst2 ]
print(lst)
4.带有判断条件的多循环推导式
lst_new = []
for i in lst1:
for j in lst2:
if lst1.index(i) == lst2.index(j):
lst_new.append(i+"♡♢"+j)
print(lst_new)
# 改写推导式
lst = [i+"♡♢"+j for i in lst1 for j in lst2 if lst1.index(i) == lst2.index(j)]
print(lst)
# 案例
lst = []
for x in range(6):
if x % 2 == 0 :
for y in range(6):
if y % 2 == 1:
lst.append( (x,y) )
print(lst)
# 推导式
lst = [ (x,y) for x in range(6) if x % 2 == 0 for y in range(6) if y % 2 == 1 ]
print(lst)
集合推导式
"""
案例:
满足年龄在18到21,存款大于等于5000 小于等于5500的人,
开卡格式为:尊贵VIP卡老x(姓氏),否则开卡格式为:抠脚大汉卡老x(姓氏)
把开卡的种类统计出来
"""
lst = [
{"name":"赵沈阳","age":18,"money":3000},
{"name":"赵万里","age":19,"money":5200},
{"name":"赵蜂拥","age":20,"money":100000},
{"name":"赵世超","age":21,"money":1000},
{"name":"王志国","age":18,"money":5500},
{"name":"王永飞","age":99,"money":5500}
]
setvar = set()
for i in lst:
print(i) # {'name': '赵沈阳', 'age': 18, 'money': 3000}
if 18 <= i["age"] <= 21 and 5000 <= i["money"] <= 5500:
res = "尊贵VIP卡老{}".format(i["name"][0])
else:
res = "抠脚大汉卡老{}".format(i["name"][0])
# 添加到集合中
setvar.add(res)
print(setvar)
# { 三元运算符 + 推导式 }
# 三运运算符 + 推导式
setvar = { "尊贵VIP卡老{}".format(i["name"][0]) if 18 <= i["age"] <= 21 and 5000 <= i["money"] <= 5500 else "抠脚大汉卡老{}".format(i["name"][0]) for i in lst }
print(setvar)
字典推导式
1. enumerate
"""
enumerate(iterable,[start=0])
功能:枚举 ; 将索引号和iterable中的值,一个一个拿出来配对组成元组,通过迭代器返回
参数:
iterable: 可迭代性数据 (常用:迭代器,容器类型数据,可迭代对象range)
start: 可以选择开始的索引号(默认从0开始索引)
返回值:迭代器
"""
# 基本语法
from collections import Iterator,Iterable
lst =["王一","吕洞宾","何仙姑","铁拐李","张国老","曹国舅","蓝采和","韩湘子"]
it = enumerate(lst)
it = enumerate(lst,start=100)
print(isinstance(it,Iterator)) # True
# next
print( next(it) )
# for + next (推荐,数据较大时使用)
for i in range(3):
print(next(it))
# for
for i in it:
print(i)
# list 强转迭代器
print(list(it))
# (1) 字典推导式 配合 enumerate 来实现
dic = {k:v for k,v in enumerate(lst,start=100)}
print(dic) # {100: '王一'}
"""
(100, '王一')
(101, '吕洞宾')
(102, '何仙姑')
(103, '铁拐李')
(104, '张国老')
(105, '曹国舅')
(106, '蓝采和')
(107, '韩湘子')
"""
# (2) 使用dict强转迭代器,瞬间得到字典
dic = dict( enumerate(lst,start=100) )
print(dic)
2. zip
"""
特点:按照索引配对
zip(iterable, ... ...)
功能: 将多个iterable中的值,一个一个拿出来配对组成元组,通过迭代器返回
iterable: 可迭代性数据 (常用:迭代器,容器类型数据,可迭代对象range)
返回: 迭代器
"""
# 基本语法
# lst1 = ["孙开启","王永飞","于朝志"]
# lst2 = ["薛宇健","韩瑞晓","上朝气"]
# lst3 = ["刘文博","历史园","张光旭"]
# 在索引下标同时存在时,才会进行配对,否则舍弃.
lst1 = ["孙开启","王永飞","于朝志"]
lst2 = ["薛宇健","韩瑞晓"]
lst3 = ["刘文博"]
it = zip(lst1,lst2,lst3)
print(list(it)) # [("孙开启","薛宇健","刘文博")]
# (1) 字典推导式 配合 zip 来实现
lst_key = ["ww","axd","yyt"]
lst_val = ["王维","安晓东","杨元涛"]
# ('ww', '王维'), ('axd', '安晓东'), ('yyt', '杨元涛')
dic = {k:v for k,v in zip(lst_key , lst_val) }
print(dic)
# (2) 使用dict强转迭代器,瞬间得到字典
dic = dict( zip(lst_key , lst_val) )
print(dic)
生成器
元组推导式是生成器(
generator
)
"""
#生成器本质是迭代器,允许自定义逻辑的迭代器
#迭代器和生成器区别:
迭代器本身是系统内置的.重写不了.
而生成器是用户自定义的,可以重写迭代逻辑
#生成器可以用两种方式创建:
(1)生成器表达式 (里面是推导式,外面用圆括号)
(2)生成器函数 (用def定义,里面含有yield)
"""
1. 生成器表达式 (里面是推导式,外面用圆括号)
gen = ( i for i in range(10) )
print(gen) # <generator object <genexpr> at 0x00>
# 判断类型
from collections import Iterator,Iterable
print(isinstance(gen,Iterator)) # True
# 1.next 调用生成器
print(next(gen))
print(next(gen))
# 2.for + next 调用生成器
for i in range(3):
print(next(gen))
# 3.for 调用生成器所有数据
for i in gen:
print(i)
# 4.list强转生成器,瞬间得到所有数据
gen = ( i for i in range(10) )
print(list(gen))
# print(next(gen)) # error # StopIteration
2. 生成器函数
"""
# yield 类似于 return
共同点在于:执行到这句话都会把值返回出去
不同点在于:yield每次返回时,会记住上次离开时执行的位置 , 下次在调用生成器 , 会从上次执行的位置往下走
而return直接终止函数,每次重头调用.
yield 6 和 yield(6) 2种写法都可以 yield 6 更像 return 6 的写法 推荐使用
"""
(1) 基本语法
def mygen():
print("111")
yield 1
print("222")
yield 2
print("333")
yield 3
# 初始化生成器函数 => 返回生成器对象 => 简称生成器
gen = mygen()
# 第一次调用
res = next(gen)
print(res) # 1
# 第二次调用
res = next(gen)
print(res) # 2
# 第三次调用
res = next(gen)
print(res) # 3
# 第四次调用,会报错
"""
# StopIteration error
res = next(gen)
print(res)
"""
"""
# 第一次调用
print("111") yield 1 保存当前第2行代码的状态,把1返回,并且等待下一次调用
# 第二次调用
从上一次保存的位置2行往下走, print("222") yield 2 保存当前第5行代码的状态,把2返回,并且等待下一次调用
# 第三次调用
从上一次保存的位置5行往下走, print("333") yield 3 保存当前第8行代码的状态,把3返回,并且等待下一次调用
# 第四次调用
因为没有更多的yield 返回数据,所有停止迭代.出现报错异常.
"""
(2) 优化生成器代码
"""生成器应用的场景是在大数据的范围中使用,切记不可直接用for遍历所有,可能无法短时间内获取所有数据"""
def mygen():
for i in range(1,101):
yield i
# 初始化生成器函数 => 生成器
gen = mygen()
# 需要多少数据,就取多少数据,防止占用大内存
for i in range(30):
num = next(gen)
print("我的球衣号码是{}".format(num))
for i in range(40):
num = next(gen)
print("我的球衣号码是{}".format(num))
(3) send
的使用方式 (给上一个yield
发送数据)
"""
# next和send区别:
next 只能取值
send 不但能取值,还能发送值
# send注意点:
第一个 send 不能给 yield 传值 默认只能写None
最后一个yield 接受不到send的发送值
"""
def mygen():
print("start")
res = yield "内部1"
print(res,"<==内部==>")
res = yield "内部2"
print(res,"<==内部==>")
res = yield "内部3"
print(res,"<==内部==>")
print("end")
# 初始化生成器函数 => 生成器
gen = mygen()
# 第一次调用生成器
"""
第一次调用生成器时,因为没有遇到yield保存的代码位置,
无法发送数据,默认第一次只能发送None
"""
res = gen.send(None) # 不传None会报错
print(res,"<==外部==>")
# 第二次调用生成器
res = gen.send("100")
print(res,"<==外部==>")
# 第三次调用生成器
res = gen.send("200")
print(res,"<==外部==>")
# 第四次调用生成器
"""
# error, print("end")后面没有yield,会报错
res = gen.send("300")
print(res,"<==外部==>")
"""
"""
使用send调用生成器,第一次发送时必须是None,因为还没有遇到yield保存的代码位置
res = gen.send(None) 走到mygen生成器函数中
print("start")
res = yield "内部1" 执行第4行 ,保存退出,记录当前代码位置,将 "内部1" 返回
在22行接受数据 res = "内部1" print(内部1,"<==外部==>")
第二次调用生成器
res = gen.send("100") 把100这个数据发送给上一次代码保存的位置4行进行接收. => 导致 4行 res = 100
打印5行 print(100 ,"<==内部==>")
执行7行 res = yield "内部2" 保存退出,记录当前代码位置,将 "内部2" 返回
执行26行 res = gen.send("100") => "内部2" print("内部2","<==外部==>")
....
依次类推 ...
到第四次调用时, 因为没有更多的yield 返回数据,gen.send(300)无法接受到返回值,所以出现停止迭代 StopIteration的报错,程序终止;
"""
(4) yield from
的使用
"""将一个可迭代对象变成一个迭代器返回 """
def mygen():
lst = ["张磊","李亚峰","刘一峰","王同培"]
yield from lst
# 初始化生成器函数
gen = mygen()
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
# print(next(gen)) # StopIteration
(5) 案例: 斐波那契数列
# (5) 斐波那契数列
"""使用生成器分段获取所有内容,而不是一股脑的把所有数据全部打印"""
"""1 1 2 3 5 8 13 21 34 .... """
def mygen(maxval):
a,b = 0,1
i = 0
while i < maxval:
# print(b)
yield b
a,b = b,a+b
i += 1
# mygen(10)
gen = mygen(10)
# 第一次获取
for i in range(3):
print(next(gen))
# 第二次获取
for i in range(5):
print(next(gen))