闭包函数
一、什么是闭包
闭包:闭是封闭(函数内部函数),包是包含(该内部函数对外部作用域而非全局作用域的变量的引用)。闭包指的是:函数内部函数对外部作用域而非全局作用域的引用。简单的说闭包函数就是把变量和函数一起包起来,下次要用直接使用
def outter(x):
def inner():
print(x)
return inner
inner = outter(2) # inner # f = inner
inner()
inner()
inner()
# 查看闭包的元素
print(F"f.__closure__[0].cell_contents: {f.__closure__[0].cell_contents}")
结果:
2
2
2
f.closure[0].cell_contents: 2
二、闭包函数的应用
闭包的含义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹一层作用域,这使得该函数无论在何处调用,优先先使用自己外层包裹的作用域
应用领域:延迟计算(原来我们是传参的,现在我们包裹起来)、怕成领域
import requests
def get(url):
response = requests.get(url)
print(f"done: {url}")
get('https://www.baidu.com')
get('https://www.baidu.com')
get('https://www.baidu.com')
get('https://www.cnblogs.com/linhaifeng')
get('https://www.cnblogs.com/linhaifeng')
get('https://www.cnblogs.com/linhaifeng')
one: https://www.baidu.com
done: https://www.baidu.com
done: https://www.baidu.com
done: https://www.cnblogs.com/linhaifeng
done: https://www.cnblogs.com/linhaifeng
done: https://www.cnblogs.com/linhaifeng
上面的方式是极其复杂的,我们如果使用默认参数也只能解决一个网址,因此我们可以考虑使用闭包的方式。
def func(url):
def get_url():
r = requests.get(url)
r.raise_for_status()
r.encoding = r.apparent_encoding
print(f'{url})
# print(r.text[:1000])
return get_url
get_baidu = func("https://www.baidu.com/")
get_baidu()
get_baidu()
get_taobao = func('https://www.taobao.com/')
get_taobao()
get_taobao()
get_taobao()
print(get_taobao.__closure__[0].cell_contents)
结果:
url:https://www.baidu.com/
url:https://www.baidu.com/
url:https://www.taobao.com/
url:https://www.taobao.com/
url:https://www.taobao.com/
get_taobao.closure[0].cell_contentshttps://www.taobao.com/
装饰器
一、无参装饰器
1.1 什么是装饰器?
装饰器,器指的是工具,二而程序中的函数就是具备某一功能的工具,所以装饰器指的是为被装饰器对象(函数)添加额外功能。因此定义装饰器就是定义一个函数,只不过该函数的功能是用来为其他函数添加额外的功能
装饰器本质就是一个函数A,装饰的对象也是一个函数B,用一个函数A去装饰一个函数B,
需要注意的是:
-
装饰器本身其实就是可以任意调用的对象
-
被装饰的对象也可以是任意可调用的对象
def A(): """装饰器""" pass def B(): """被装饰的对象""" pass B()
1.2 为什么要用装饰器?
如果我们已经上线了一个项目,我们需要修改某一个方法,但是我们不想修改方法的使用方法,这个时候可以使用装饰器。因为软件的维护应该遵循开放封闭原则,即软件一旦上线运行后,软件的维护对修改源代码是封闭的,对扩展功能指的是开放的。
装饰器的实现必须遵循两大原则:
- 不修改被装饰对象的源代码
- 不修改被装饰对象的调用方式
装饰器其实就是在遵循以上两个原则的前提下为被装饰对象添加新功能。
1.3 如何使用装饰器
def index():
"""被装饰的函数"""
print('hello, index')
index()
对上面函数实现:
-
打印函数运行的时
1.1 改变了函数体代码,没改变调用方式
import time def index(): start = time.time() print("hello, index") time.sleep(1) end = time.time() print(end - start) index()
1.2 没改变调用方式,也没改变源码,但是不通用
import time def index(): print("hello, index") start = time.time() index() time.sleep(1) end = time.time() print(end - start)
-
装饰器
检测index的运行的时间,但是不能改变index的调用方式,以及index的源码
def deco(func): # func = 真正的index
"""装饰器函数"""
def inner():
start = time.time()
func() # 真正的index()
time.sleep(1)
end = time.time()
print(ent - start)
return inner
def index():
"""被装饰的函数"""
print("hello, index")
# 实现index函数调用
index = deco(index) # index = inner
index() # f1()
二、装饰器语法糖
在被装饰函数正上方,并且是单独一行写上@装饰器名
def deco(func): # func = 真正的index
"""装饰器函数"""
def inner():
start = time.time()
func() # 真正的index()
time.sleep(1)
end = time.time()
print(ent - start)
return inner
def index():
"""被装饰的函数"""
print("hello, index")
@deco
index()
三、装饰器模板
def outter(func):
def inner(*args, **kwagrs):
res = func(*args, **kwagrs)
return res
return inner
def deco(func):
"""装饰器"""
def inner(*args, **kwargs): # inner是未来要运行的函数
res = func(*args, **kwargs) # func实际调用的函数
return res
return inner
@deco
def index(x, a=12):
"""被装饰的对象"""
print('x:',x,)
print("a:", a)
print('hello index')
return index
res = index()
print(res)
实现一个用户登录装饰器。
def login(func):
def ingfunc(*args, **kwargs):
username = input("请输入用户名:")
userpwd = input("请输入密码:")
if username == 'randy' and userpwd:
print("登录成功")
res = func(*args, **kwargs)
else:
print('失败')
return res
return ingfunc
@login
def shopping():
print('shoppoing')
shopping()
四、三层装饰器
# 三层装饰器
def sanceng(engine):
def outter(func):
def wrapper(*args, **kwargs):
# 加功能
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('usrename:')
pwd = input('pwd:')
if username == 'nick' and pwd == '123':
print('登录成功')
res = func(*args, **kwargs) # shopping() return res
else:
print('登录失败')
elif engine == 'db':
print('账号密码来自于数据库,非法请求')
return inner
return login
@auth('db')
def shopping():
print('shopping')
login = auth('db') # login = login
shopping = login(shopping) # shopping = inner
shopping() # inner()
# 叠加多个装饰器
# 1. 加载顺序(outter函数的调用顺序):自下而上
# 2. 执行顺序(wrapper函数的执行顺序):自上而下
迭代器
一、迭代器的概念
迭代器即迭代的工具,那什么是迭代呢?
迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值
while True: #只是单纯地重复,因而不是迭代
print('===>')
l=[1,2,3]
count=0
while count < len(l): #迭代
print(l[count])
count+=1
二、可迭代对象
python中一切皆对象,对于这一切的对象中,但凡有__iter__
方法的对象,都是可迭代对象。
基于上一次的结果推出下一次结果
#
# 可迭代(具有__iter__方法)对象
x = 2 # 不可迭代
# x.__iter__ # SyntaxError: invalid syntax
s = 'randysun'
s.__iter__() # str 为可迭代对象
lt = [1, 2, 3]
lt.__iter__() # list 为可迭代对象
dic = {'a': 1, "b": 2}
dic.__iter__() # dict 为可迭代对象
tup = (1,)
tup.__iter__() # tuple 为可迭代对象
se = {1, 2, 3}
se.__iter__() # 可迭代对象
f = open('test.py')
f.__iter__() # 可迭代对象
def func():
pass # 不可迭代对象
# 有__iter__()方法的对象都是可迭代对象,然后除了数字类型和函数之外都是可迭代对象
可迭代的对象:Python内置str、list、tuple、dict、set、file都是可迭代对象。
s = 'randysun'
s_iter = s.__iter__()
print(s_iter.__next__())
print(s_iter.__next__())
print(s_iter.__next__())
特点:
- 内置有
__iter__
方法的都叫可迭代的对象。
二、迭代器对象
迭代器对象: 具有__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') # 可迭代对象,迭代器对象
# 只有文件是迭代器对象
lt = [1, 2, 3]
lt_iter = lt.__iter__()
while True:
try:
print(lt_iter.__next__())
except StopIteration:
break
1
2
3
for i in lt: # 可迭代对象;迭代器对象 不依赖索引取值,而是迭代取值
print(i)
# 1. 首先使用iter把it变成迭代器对象,对文文件也要使用iter方法把文件再一次iter
# 2. 然后使用next方法进行迭代取值
# 3. 判断StopIteration异常,遇到异常终止
-
可迭代对象:具有__iter__方法的对象就是可迭代对象,除了数字类型和函数都是可迭代对象
-
可迭代器对象:具有__iter__和__next__方法的都是迭代器对象,只有文件
-
迭代器对象一定是可迭代对象,可迭代对象不一定是可迭代器对象
-
for循环 == 迭代循环
- 把lt(可迭代对象/迭代器对象)用__iter__方法转换成迭代器对象
- 使用__next__取出迭代器里的所有值
- 使用__next__方法取尽迭代器中的所有值,一定会报错,通过异常捕捉退出while循环
- 解决了不依赖索引取值
-
优缺点
-
优点:
- 提供一种统一的、不依赖于索引的迭代方式
- 惰性计算,节省内存
-
缺点:
- 无法获取长度(只有在next完毕才知道到底有几个值)
- 一次性的,只能往后走,不能往前退