python三大器
一.迭代器
迭代:迭代是一个重复的过程,每一次重复都是基于上一次的结果而来
下面给出两个例子区分:
# 单纯的重复,每一次的重复都与上次的结果无关,因此不叫迭代
while True:
print("----------")
# 此例子属于迭代,因为每次重复的结果都和上次相关
l = ['a','b','c']
n = 0
while n < len(l):
print(l[n])
n +=1
可迭代对象:
定义:在python中,凡是有__iter__方法的对象,都是可迭代对象
类型:str,list,tuple,dict,set,file
--判断一个对象是否是可迭代对象
from collections import Iterable
isinstance([],Iterable)
优点:1.使用灵活 2.直接查看值
缺点:浪费内存
迭代器对象:
定义:迭代取值工具,可迭代的对象执行__iter__方法得到的返回值就是迭代器对象
--有__iter__和__next__两种方法:
--执行__next__方法,得到迭代器对象中的一个值
--执行__iter__方法:拿到迭代器本身
获取方式:执行可迭代对象的__iter__方法,拿到的返回值就是迭代器对象
优点:1.打破了索引取值的方式,提供一种统一的取值方式
2.更加节省内存(python3中range()返回range对象,而非列表)
缺点:1.取值麻烦,只能一个一个取,只能从前往后取,并且是一次性的(如果一个while循环取完了,那么第二个循环接着取是娶不到的,需要重新调用__iter__方法)
可迭代对象和迭代器区别
可迭代对象不一定是迭代器对象,但迭代器对象一定是可迭代对象
文件本身就是迭代器对象
for循环本质
'''
dic = {'x':1,'y':2}
for item in dic #for调用dic对象的__iter__方法得到迭代器对象,循环此迭代器对象的__next__方法,将得到的值赋给item
print(item)
'''
# while循环实现for循环本质
lst = [1,2,3,4,5]
d_lst = lst.__iter__()
while True:
try:
print(d_lst.__next__())
except StopIteration as e:
break
总结:
#迭代器对象的__iter__()方法得到迭代器本身
x="hello"
iter_x = x.__iter__()
print(iter_x.__iter__() is iter_x) # True
a="sfdfretgfd"
# print(a.__iter__())
sf=a.__iter__() #可迭代对象a执行__iter__方法得到返回值sf为迭代器对象
print(sf.__next__()) #迭代器对象的__next__方法返回值
print(sf.__next__())
print(sf.__next__())
二.生成器
2.1生成器初始
# 生成器不能通过下标取值。
生成器的本质就是一个迭代器。
迭代器和生成器的区别:一个是python自带的,一个是程序员自己写的
2.2生成器的构建方式
在python中有三种方式来创建生成器:
1.通过生成器函数
2.通过生成器推导式
3.python内置函数或者模块提供(其实1,3两种本质上差不多,都是通过函数的形式生成,只不过1是自己写的生成器函数,3是python提供的生成器函数而已)
2.3生成器函数
首先,我们来看一个简单函数:
def func():
print(11)
return 22
ret = func()
print(ret)
# 运行结果:
11
22
将函数中的return换成yield,这样func就不是函数了,而是一个生成器
def func():
print(11)
yield 22
打印一下,看获取到的是什么
ret = func()
print(ret)
# 运行结果:
<generator object func at 0x000001A575163888>
由上可知,在函数中添加yield,此函数就是一个生成器。
2.4yield和return的区别
return一般在函数中只设置一个,他的作用是终止函数,并且给函数的执行者返回值。
yield在生成器函数中可设置多个,他并不会终止函数,next会获取对应yield生成的元素。
下面给出两个例子区分:
def func():
print(10)
return 10
print(func())
#打印结果:
10
def func():
print(10)
yield 10
print(20)
yield 20
ret = func() #此处函数名添加括号不执行(普通函数执行,在生成器中函数中是新建生成器)
print(ret.__next__()) #执行到yield 10,执行完之后阻塞,等待下次next
print(ret.__next__()) #继续执行yield 10下面的代码
#打印结果
10
10
20
20
2.5坑
def func():
for i in range(10):
yield i
print(func().__next__())
print(func().__next__())
print(func().__next__())
#打印结果
0
0
0
坑解释:如果函数中有yield,就不是一个函数,而是生成器,函数名加括号就是新建一个生成器,每次打印都新建生成器,因此都从头开始。
#正确做法:
ret = func()
print(ret.__next__())
print(ret.__next__())
#打印结果:
0
1
2.6 yield from
在python3中提供一种可以直接把可迭代对象中的每一个数据作为生成器的结果返回
def func():
lst1 = ["牛羊配","老奶奶花生米","卫龙","虾扯蛋","米老头","老干妈"]
lst2 = ["小浣熊","老干爹","亲嘴烧","麻辣烫","黄焖鸡","井盖"]
yield from lst1
yield from lst2
g = func()
print(next(g))
print(next(g))
#打印结果:
牛羊配
老奶奶花生米
#和上例子同理
def func():
lst1 = ["牛羊配","老奶奶花生米","卫龙","虾扯蛋","米老头","老干妈"]
lst2 = ["小浣熊","老干爹","亲嘴烧","麻辣烫","黄焖鸡","井盖"]
for i in lst1:
yield i
for j in lst2:
yield j
#调用
ret = func()
for i in ret:
print(i)
三.装饰器
3.1开放封闭原则
1.对扩展开放:我们必须允许代码扩展、添加新功能。
2.对修改封闭:对自身已有代码的修改持封闭原则
----简而言之:对外开放,对内封闭
3.2定义
在不改变函数内源代码及其调用方式的前提下,为其添加新功能,添加的内容就是装饰器。我们可以得知装饰器满足上述两个条件:1.不改变源代码 2.不改变调用方式。
3.3无参推导
版本一
import time
def fun1():
time.sleep(1)
print("welcome to fun1")
def fun2():
time.sleep(2)
print("welcome to fun2")
# 要想测试fun1和fun2函数执行的时间,继续在程序内部添加代码
start_time = time.time()
fun1()
end_time = time.time()
print(end_time - start_time)
start_time = time.time()
fun2()
end_time = time.time()
print(end_time - start_time)
# 代码如上写的话,会有冗余,因此我们要将其封装成函数,减少冗余
版本二
import time
def fun1():
time.sleep(1)
print("welcome to fun1")
def fun2():
time.sleep(2)
print("welcome to fun2")
# 要想测试fun1和fun2函数执行的时间,继续在程序内部添加代码
def wrapper(f):
start_time = time.time()
f()
end_time = time.time()
print(end_time - start_time)
wrapper(fun1)
wrapper(fun2)
# 此时,即使已经完成了功能,改变了函数的调用方式(原来是fun1()/fun2()),不符合装饰器基本,因此衍生出版本三,采用闭包的形式
版本三
import time
def fun1():
time.sleep(1)
print("welcome to fun1")
def fun2():
time.sleep(2)
print("welcome to fun2")
# 要想测试fun1和fun2函数执行的时间,继续在程序内部添加代码
def wrapper(f):
def inner():
start_time = time.time()
f()
end_time = time.time()
print(end_time - start_time)
return inner
fun1 = wrapper(fun1) # fun1 = inner,由此可知(分为两步:1.执行wrapper 2.赋值)
fun1() # inner()
fun2 = wrapper(fun2) # fun2 = inner
fun2() # inner()
# 由上可知:此时执行fun1(),就是执行inner()方法
终极版(加语法糖)
# 装饰器的本质就是调用inner函数
def wrapper(f): # f为要被装饰的函数名
def inner(*args,**kwargs): # 参数为f()中的形参
# 函数装饰前
f(*args,**kwargs)
# 函数装饰后
return inner
@wrapper # fun = wrapper(fun)
def fun(*args,*kwargs):
time.sleep(2)
print("welcome to fun")
fun("你好") #调用inner函数
3.4有参装饰器
有参装饰器就是在无参装饰器外面再套一层,参数为qq、微信之类的,用于对用户不同方式登录的装饰。
dic_user = {
"username":"alex",
"password":"alex123",
"flag":False
}
def outter(argv): # argv可选参数
def wrapper(fun): # fun为被装饰函数名
def inner(*args,**kwargs):
if argv == "qq":
print("欢迎进入QQ界面")
fun()
elif argv == "wechat":
print("欢迎进入微信界面")
fun()
return inner
return wrapper
# @outter("qq") 内层函数名 = 外层函数名(参数) 被装饰函数名 = 内层函数名(被装饰函数名)
@outter("qq") # wrapper = outter("qq") fun1_qq = wrapper(fun1_qq)
def fun1_qq(*args,**kwargs):
print(f"欢迎{args}进入qq")
# wrapper = outter("qq")
# fun1_qq = wrapper(fun1_qq)
fun1_qq("小明")
3.5多个装饰器装饰一个函数
3.5.1不带return(语法糖执行顺序:wrapper3->wrapper2->wrapper1)
def wrapper1(fun):
def inner1():
print("装饰器开始1") #1
fun() #7
print("装饰器结束1") #6
return inner1
def wrapper2(fun):
def inner2():
print("装饰器开始2") #2
fun()
print("装饰器结束2") #5
return inner2
def wrapper3(fun):
def inner3():
print("装饰器开始3") #3
fun()
print("装饰器结束3") #4
return inner3
@wrapper1
@wrapper2
@wrapper3
def func():
print("被装饰函数")
#func = wrapper3(func)
#func = wrapper2(func)
#func = wrapper1(func)
func()
#打印结果(上面数字):
装饰器开始1
装饰器开始2
装饰器开始3
被装饰函数
装饰器结束3
装饰器结束2
装饰器结束1
3.5.2带return
def wrapper1(fun):
def inner1():
print("装饰器开始1")
ret = fun()
print("装饰器结束1")
return ret
return inner1
def wrapper2(fun):
def inner2():
print("装饰器开始2")
ret = fun()
print("装饰器结束2")
return ret
return inner2
@wrapper1
@wrapper2
def func():
return "被装饰函数"
# func = wrapper2(func) # func = inner2
# func = wrapper1(func) # func = wrapper1(inner2)
print(func()) # wrapper1(inner2)()
# 打印结果(因为print(func())先执行,后打印):
装饰器开始1
装饰器开始2
装饰器结束2
装饰器结束1
被装饰函数