超强台风利奇马就这样和上海擦肩而过了,今天的天气依旧艳阳高照,不幸的是我的扁桃体发炎了,又肿又痒,如万千蚂蚁在嗓子里爬动,买盒金嗓子,坚持学习。今天学了闭包函数、装饰器、迭代器,下面总结一下今天的知识点。
一、闭包函数
1、什么是闭包?
-
闭包:闭是封闭(函数内部函数),包是包含(该内部函数对外部作用域而非全局作用域的变量的引用。
闭包指的是:函数内部函数对外部作用域而非全局作用域的引用。
-
闭包函数:传参的另外一种方式,把函数+参数包在一起返回出去。
def outter():
x=1
def inner():
print(x)
return inner
f=outter() # f接收outter的返回值inner,其中包含inner+x
def f2():
x=2
f() # =inner(),先在inner中找x的值,没有就到outter中找,找到x=1,输出
f2()
---------------------------------------------------
最后输出结果为1
1.1 两种为函数传参的方式
方式一:使用参数的形式
def func(x):
print(x)
func(1) # 把实参1传给形参x
func(1) # 把实参1传给形参x
-----------------------------------------------------------
1
1
这种方式比较麻烦,每次传参都得在func()中给定参数
方式二:包给函数
# 函数固定格式
def outter(x):
x=1
def inner():
print(x)
return inner
f=outter(1) #得到返回值inner,其中包着inner+x=1
# 如果f=outter(2),输出还是1,因为查找顺序从print(x)当前位置开始,再到局部outter中x=1,不会再去找到传参的参数
f() # =inner(),其中x=1
f()
f()
-----------------------------------------------------------
1
1
1
2、闭包函数的应用
- 闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得该函数无论在何处调用,优先使用自己外层包裹的作用域。
- 应用领域:延迟计算(原来我们是传参,现在是把函数和变量一起包起来)、爬虫领域。
例如要定义一个爬起网站的闭包函数:
def outter(url):
def get_res():
res=requests.get(url)
print(res.text)
return get_res
baidu_spider=outter('https://www.baidu.com') # baidu_spider=ger_res+url='https://www.baidu.com',直接就把'https://www.baidu.com'传给了url
baidu_spider() # =get_res(),其中url='https://www.baidu.com'
# 这种想改网址,可以直接在调用阶段改实参
taobao_spider=outter('https://www.taobao.com')
taobao_spider()
二、装饰器
1、什么是装饰器
器指的是工具,而程序中的函数就是具备某一功能的工具,所以装饰器指的是为被装饰函数添加额外功能。装饰器本质就是一个函数A,被装饰的对象也是一个函数B,用函数A去装饰函数B。
注意:
- 装饰器本身其实是可以任意可调用的对象
- 被装饰的对象也可以是任意可调用的对象
2、装饰器的实现的两大原则
1、不修改被装饰对象的源代码
2、不修改被装饰对象的调用方式
3、装饰器模板
应用举例:打印程序运行时间
def deco(func):
"""装饰器函数"""
def f1():
start=time.time()
func()
end=time.time()
print(end-start)
return f1
import time
def index():
"""被装饰函数"""
print('hello index')
time.sleep(1)
index=deco(index) # 把index传给deco的形参func,这里的前一个index=f1(),其中func=index(被装饰的函数名index)
index()
装饰器模板:
def deco(func):
def wrapper(*args,**kwargs):
# 这里加要添加的功能
res=func(*args,**kwargs)
return res
return wrapper
@deco
def shopping():
print('shopping')
4、装饰器语法糖
在被装饰函数正上方,并且是单独一行写上@装饰器名
def deco(func):
"""装饰器函数"""
def f1():
start=time.time()
func()
end=time.time()
print(end-start)
return f1
@deco # 语法糖(更精简的代码),代替index=deco(index),可以用@deco()给deco传参
import time
def index():
"""被装饰函数"""
print('hello index')
time.sleep(1)
# index=deco(index)
index()
5、三层装饰器
5.1 闭包包三层的运用
def f1(y):
def f2():
x = 1
def f3():
print(f"x: {x}")
print(f"y: {y}")
return f3
return f2
f2 = f1(2)
f3 = f2()
f3()
x: 1
y: 2
5.2 三层装饰器:给双层装饰器加参数
5.3 三层装饰器模板
def sanceng(engine):
def outter(func):
def wrapper(*args, **kwargs): # wrapper是未来要运行的函数
# 加功能
print(engine)
res = func(*args, **kwargs) # func是被装饰的函数
return res
return wrapper
return outter
@sanceng('file')
def shopping():
print('shopping')
例如:判断账号密码来自于哪个地方
def auth(engine):
def login(func):
def inner(*args,**kwargs):
# 给购物函数添加登录功能
if engine=='file':
username=input('username:')
pwd=input('pwd:')
if username=='zyl'and pwd=='123':
print('登录成功')
res=func(*args,**kwargs)
return res
else:
print('登录失败')
elif engine=='db':
print('账号密码来自于数据库,非法请求')
return inner
return login
@ auth('file')
def shopping():
# 被装饰函数
print('shopping')
shopping()
三、迭代器
迭代器:迭代的工具。更新换代、重复,基于上一次的结果推出下一次的结果
3.1 可迭代对象
定义:具有__iter__
方法的对象就是可迭代对象,除了数字类型和函数类型
可迭代对象无论迭代多少次,他的内存地址是不变的
注意:tuple(1)与tuple(1,)类型有区别
# 可迭代(具有__iter__方法) 对象
x = 1 # 不可迭代对象
s = 'nick' # 可迭代对象
s.__iter__()
lt = [1, 2, 3] # 可迭代对象
dic = {'a': 1, 'b': 2} # 可迭代对象
tup = (1,) # 元组只有一个元素必须得加逗号# 可迭代对象
se = {1, 2, 3} # 可迭代对象
f = open('time.py') # 可迭代对象
def func(): # 不可迭代对象
pass
# 有__iter__()方法的对象就是可迭代对象,然后除了数字类型和函数之外都是可迭代对象
s = 'nick'
s_iter = s.__iter__()
print(s_iter.__next__()) # 基于索引(基于上一次结果)通过__next__进行迭代
print(s_iter.__next__())
print(s_iter.__next__())
3.2 迭代器对象
只有字符串和列表都是依赖索引取值的,而其他的可迭代对象都是无法依赖索引取值的。因此我们得找到一个方法能让其他的可迭代对象不依赖索引取值。
在找到该方法前,首先我们给出迭代器对象的概念:可迭代的对象执行__iter__
方法得到的返回值。并且可迭代对象会有一个__next__
方法。
# 不依赖索引的数据类型迭代取值
dic = {'a': 1, 'b': 2, 'c': 3}
iter_dic = dic.__iter__()
print(iter_dic.__next__())
print(iter_dic.__next__())
print(iter_dic.__next__())
# print(iter_dic.__next__()) # StopIteration:
----------------------------------------------------------------
a
b
c
# 依赖索引的数据类型迭代取值
lis = [1, 2, 3]
iter_lis = lis.__iter__()
print(iter_lis.__next__())
print(iter_lis.__next__())
print(iter_lis.__next__())
# print(iter_lis.__next__()) # StopIteration:
----------------------------------------------------------------
1
2
3
上述的方法是非常繁琐的,我们可以使用while循环精简下。其中使用的try...except...
为异常处理模块
s = 'hello'
iter_s = s.__iter__()
while True:
try:
print(iter_s.__next__())
except StopIteration:
break
----------------------------------------------------------------
h
e
l
l
o
# 迭代器对象: 具有__iter__以及__next__方法的叫做迭代器对象
s = 'nick' # 可迭代对象,不属于迭代器对象
s.__iter__()
lt = [1, 2, 3] # 可迭代对象,不属于迭代器对象
dic = {'a': 1, 'b': 2} # 可迭代对象,不属于迭代器对象
tup = (1,) # 元组只有一个元素必须得加逗号# 可迭代对象,不属于迭代器对象
se = {1, 2, 3} # 可迭代对象,不属于迭代器对象
f = open('time.py') # 可迭代对象,迭代器对象
# 只有文件是迭代器对象
总结:
迭代器对象:执行可迭代对象的__iter__
方法,拿到的返回值就是迭代器对象。
特点:
- 内置
__next__
方法,执行该方法会拿到迭代器对象中的一个值 - 内置有
__iter__
方法,执行该方法会拿到迭代器本身 - 文件本身就是迭代器对象。
缺点:
- 取值麻烦,只能一个一个取,并且只能往后取,值取了就没了
- 无法使用len()方法获取长度
可迭代对象: 具有__iter__
方法的对象就是可迭代对象,除了数字类型和函数都是可迭代对象
迭代器对象: 具有__iter__
和__next__
方法的都是迭代器对象,只有文件
迭代器对象一定是可迭代对象; 可迭代对象不一定是迭代器对象
四、for循环原理
for循环称为迭代循环,in后必须是可迭代的对象。
lt = [1,2,3]
lt_iter = lt.__iter__() # 将它变为迭代器对象
while 1:
try:
print(lt_iter.__next__())
except StopIteration: # 对他进行异常捕捉
break
使用for循环
for i in lt: # 可迭代对象;迭代器对象 # 不依赖索引取值,而是迭代取值
print(i)
'''
1. 把lt(可迭代对象/迭代器对象)用__iter__方法转换成迭代器对象
2. 使用__next__取出迭代器里的所有值
3. 使用__next__方法取尽迭代器中的所有值,一定会报错,通过异常捕捉退出while循环
'''