闭包函数&装饰器&迭代器
闭包函数
闭包函数是函数传参的一种方式。它可以把变量和函数一起包起来,下次要直接调用
我们先来看一下普通的传参方式:
def inner(x):
print(x)
inner(1)
inner(1)
inner(1)
看上去好像也蛮简单的,但是如果你要传递的值会很多呢,比如要传递摸一个复杂的网址,那么就会变成:
inner(https://image.baidu.com/search/detailct=503316480&z=undefined&tn=baiduimagedetail&ipn=d&word=python&step_word=&ie=utf8&in=&cl=2&lm=-1&st=undefined&hd=undefined&latest=undefined©right=undefined&cs=2274312350,3438054066&os=1842179554,428481594&simid=4207124190,751379936&pn=4&rn=1&di=93940&ln=1299&fr=&fmq=1565592554366_R&fm=&ic=undefined&s=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&is=0,0&istype=0&ist=&jit=&bdtype=0&spn=0&pi=0&gsm=0&objurl=http%3A%2F%2Fwww.haoracle.com%2Fuploads%2Fimages%2F181214%2F1544751939757415.jpg&rpstart=0&rpnum=0&adpicid=0&force=undefined&ctd=1565592579900^3_1247X953%1)
???如果要多传几遍岂不是人都傻了。。
所以这个时候就可以用到闭包函数了:
def outter(x):
def inner():
print(x)
return inner
inner = outter(2) # inner # f = inner
inner()
inner()
inner()
它将所需要的函数A封装在另一个函数B里,调用的时候只要简单的调用函数B就行了
简单来说,闭包函数是传参的另外一种方式, 参数+函数包在一起返回出去
这个时候可能又有人要问了,我传参的时候不是只要Ctrl+c, Crtl+v 不就好了?其实闭包函数主要应用就是装饰器,接下来我会一一讲解。
装饰器
装饰器是装饰的工具(函数),具有装饰别的函数的作用
装饰器本质就是一个函数A,装饰的对象也是一个函数B,用一个函数A去装饰一个函数B
它的结构大概是这样的:
def A():
"""装饰器"""
pass
def B():
"""被装饰的对象"""
pass
B()
那么又会有人问了,我用别的方法也可以办到装饰别的函数啊?
但是那些方法都不如装饰器来的简洁明了。接下来我就要将那些方法和装饰器进行比较
首先先给定一个函数index
import time
def index():
"""被装饰的函数"""
print('hello, index')
sleep.sleep(1)
index()
接下来要给这个函数装上打印他运行时间的功能,应该怎么做呢
方法一:改变主体代码
import time
def index():
start = time.time()
print('hello, index')
time.sleep(1)
end = time.time()
print(end - start)
index()
这种方法看似简单,却不实用。如果你需要在很多段代码中加上打印运行时间这一功能的的话,每段代码都改即浪费时间,还可能出现,也大大增加了工作量,所以是不可取的
方法二:改变调用方式
import time
def index():
"""被装饰的函数"""
print('hello, index')
start = time.time()
index()
time.sleep(1)
end = time.time()
print(end-start)
这种方法虽然不再改变函数本身了,但是却改变了函数的调用方式,当你以后再需要为别的函数添加功能,依旧要打一大段的代码,这肯定是不可取的
这里我就将隆重介绍Python的装饰器功能了。在此顺便提一句,装饰器是Python独有的功能,只此一家,别无分号。
import time
def deco(func): # func = index
"""装饰器函数"""
def inner():
start = time.time()
func() # index
time.sleep(1)
end = time.time()
print(end - start)
return inner
def index():
"""被装饰的函数"""
print('hello, index')
def index1():
print('hello, index1')
index = deco(index) # index = inner
index()
index1 = deco(index1)
index1()
你会发现,无论是函数本身还是调用方式,都没有被更改。如果下次再想为其他函数添加功能,只要简单的语句就可以完成
二层装饰器
如果你能充分理解上面这段代码,就可以先恭喜你已经踏出装饰器的第一步了。
这时候有的人又会问了,如果我想往函数里丢参数应该怎么办?我想丢多个参数应该怎么办?
这个时候之前学的可变长参数就派上用处了
import time
def deco(func):
def f1(*args, **kwargs):
print('args:',args) # (10,)
print('kwargs:',kwargs)
start = time.time()
# *args = *(10,) 10
res = func(*args, **kwargs) # 真正的index()
end = time.time()
print(end - start)
return res
return f1
def index(x, a=1):
print('x', x)
print('a', a)
print('hello index')
time.sleep(1)
return 123
# 重新创建的index = deco(index真正的index)
index = deco(index) # index = f1
index(10) # f1(1)
这个时候无论你原本的函数中需要多少个参数,装饰器都可以对它进行装饰了
如果这个时候你已经对代码感到困惑的时候,也不要紧,可以先了解一下装饰器的模板,以后再慢慢摸索
def login(func):
def iwrapper(*args, **kwargs):
# 想要装饰的内容
res = func(*args, **kwargs) # shopping()
return res
return wrapper
语法糖
如果你觉得装饰器已经很简单了,那么只有你想不到,没有Python办不到的。你甚至只要在原函数前加上@deco,就可以实现装饰器的功能了。
import time
def deco(func):
def f1(*args, **kwargs):
print('args:',args) # (10,)
print('kwargs:',kwargs)
start = time.time()
# *args = *(10,) 10
res = func(*args, **kwargs) # 真正的index()
end = time.time()
print(end - start)
return res
return f1
@deco # 语法糖(更精简的代码) index = deco(index)
def index(x, a=1):
print('x', x)
print('a', a)
print('hello index')
time.sleep(1)
index(10)
三层装饰器
那么可能又有人会问了,如果我想给二层装饰器加上参数该怎么办?虽然不知道是谁会怎么多事,但是Python也考虑到了这一点。
接下来我们如果给购物车的shopping功能加上登录功能,并判断是‘db’还是‘file’
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')
这样就能将登录装饰给其他函数了
三层装饰器模板:
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 outter1(func): # func = wrapper2
def wrapper1(*args, **kwargs): # wrapper是未来要运行的函数
print('------------')
res = func(*args, **kwargs) # func是被装饰的函数 # wrapper2
print('------------')
return res
return wrapper1
def outter2(func): # func = index
def wrapper2(*args, **kwargs): # wrapper是未来要运行的函数
print('11111111111111')
res = func(*args, **kwargs) # func是被装饰的函数 # index()
print('11111111111111')
return res
return wrapper2
# @outter1 # index = outter1(index)
# @outter2 # index = outter2(index) # 先运行最下面的装饰器
# # index
def index():
print('index')
index()
结果为:
------------
11111111111111
index
11111111111111
------------
由此可见,当多个装饰器装饰同一个函数时,运行顺序是从下往上的,并且第二个装饰器装饰的已经不是原来的函数了,而是已经被第一个装饰器装饰过得函数了。
迭代器
什么是迭代?迭代就是更新换代,重复,给予上次的结果推出下次的结果
可迭代对象
可迭代对象是具有__iter__方法的对象
我们可以通过判断一个数据类型是否有iter方法来知道他是否是可迭代对象
x = 1 # 不可迭代对象
s = 'hyc' # 可迭代对象
lt = [1, 2, 3] # 可迭代对象
dic = {'a': 1, 'b': 2} # 可迭代对象
tup = (1,) # 元组只有一个元素必须得加逗号# 可迭代对象
se = {1, 2, 3} # 可迭代对象
f = open('time.py') # 可迭代对象
def func(): # 不可迭代对象
pass
可见除了数字类型和函数之外都是可迭代对象
同时可迭代对象可以通过 __next__来迭代
迭代器对象
具有__iter__和__next__方法的都是迭代器对象
s = 'hyc' # 可迭代对象,不属于迭代器对象
lt = [1, 2, 3] # 可迭代对象,不属于迭代器对象
dic = {'a': 1, 'b': 2} # 可迭代对象,不属于迭代器对象
tup = (1,) # 元组只有一个元素必须得加逗号# 可迭代对象,不属于迭代器对象
se = {1, 2, 3} # 可迭代对象,不属于迭代器对象
f = open('time.py') # 可迭代对象,迭代器对象
可见迭代器对象只有文件
其实for循环就是一个迭代,我们可以用iter来实现
lt = [1,2,3]
lt_iter = lt.__iter__()
while 1:
try:
print(lt_iter.__next__())
except StopIteration:
break
结果为:
1
2
3
- 首先使用iter把lt变成迭代器对象;对于文件也要使用iter方法吧文件再一次iter下
- 然后使用next方法进行迭代取值
- 判断StopIteration异常,遇到异常终止
可以发现,这样就解决了不依赖索引取值的问题
总的来说,今天的难点依旧是之前的装饰器,但是如果暂时无法解决的话,可以先记个模板,熟练运用。