函数的执行时,*打散。
函数的定义时,*聚合。
from functools import wraps
def wrapper(f): # f = func1
@wraps(f)
def inner(*args,**kwargs): #聚合
#args (1,2,3)
'''执行函数之前的相关操作'''
ret = f(*args,**kwargs) # 打散 1,2,3
'''执行函数之后的相关操作'''
return ret
return inner
@wrapper # func1 = wrapper(func1) func1 = inner
def func1(*args): #args (1,2,3) 聚合
print(666)
return args
print(func1(*[1,2,3])) # inner(3,5) 打散
执行输出:
666
(1, 2, 3)
一、函数的有用信息
1.函数名 使用name方法获取
2.函数的解释 使用doc_方法获取
举个例子
def func1():
"""
此函数是完成登陆的功能,参数分别是...作用。
:return: 返回值是登陆成功与否(True,False)
"""
print(666)
# print(func1.__name__)
# print(func1.__doc__)
return True
func1()
print(func1.__name__) #获取函数名
print(func1.__doc__) #获取函数名注释说明
执行输出:
666
func1
此函数是完成登陆的功能,参数分别是…作用。
:return: 返回值是登陆成功与否(True,False)
这个有什么用呢?比如日志功能,需要打印出谁在什么时间,调用了什么函数,函数是干啥的,花费了多次时间,这个时候,就需要获取函数的有用信息了
带装饰器的函数
def wrapper(f): # f = func1
def inner(*args,**kwargs): #聚合
#args (1,2,3)
'''执行函数之前的相关操作'''
ret = f(*args,**kwargs) # 打散 1,2,3
'''执行函数之后的相关操作'''
return ret
return inner
@wrapper
def func1():
"""
此函数是完成登陆的功能,参数分别是...作用。
:return: 返回值是登陆成功与否(True,False)
"""
print(666)
return True
func1()
print(func1.__name__)
print(func1.__doc__)
执行输出:
666
inner
执行函数之前的相关操作
咦?为什么输出了 inner,我要的是 func1 啊。因为函数装饰之后,相当于执行了 inner 函数,所以输出 inner
为了解决这个问题,需要调用一个模块 wraps
wraps 将 被修饰的函数(func1) 的一些属性值赋值给 修饰器函数(wrapper) ,最终让属性的显示更符合我们的直觉
完整代码如下:
from functools import wraps
def wrapper(f): # f = func1
@wraps(f) #f 是被装饰的函数
def inner(*args,**kwargs): #聚合
#args (1,2,3)
'''执行函数之前的相关操作'''
ret = f(*args,**kwargs) # 打散 1,2,3
'''执行函数之后的相关操作'''
return ret
return inner
@wrapper
def func1():
"""
此函数是完成登陆的功能,参数分别是...作用。
:return: 返回值是登陆成功与否(True,False)
"""
print(666)
return True
func1()
print(func1.__name__)
print(func1.__doc__)
执行输出:
666
func1
此函数是完成登陆的功能,参数分别是…作用。
:return: 返回值是登陆成功与否(True,False)
二、带参数的装饰器
import time
def timmer(*args,**kwargs):
def wrapper(f):
print(args, kwargs) #接收第 1 步的值
def inner(*args,**kwargs):
if flag:
start_time = time.time()
ret = f(*args,**kwargs)
time.sleep(0.3)
end_time = time.time()
print('此函数的执行效率%f' % (end_time-start_time))
else:
ret = f(*args, **kwargs)
return ret
return inner
return wrapper
flag = True
@timmer(flag,2,3) # 两步:1,timmer(flag,2,3) 相当于执行 wrapper 2.@wrapper 装饰器 func1 = wrapper(func1)
def func1(*args,**kwargs):
return 666
print(func1())
执行输出:
(True, 2, 3) {}
此函数的执行效率 0.300183
666
代码分析
import time #1.加载模块
def timmer(*args,**kwargs): #2.加载变量 5.接收参数 True,2,3
def wrapper(f): #6.加载变量 8.f = func1
print(args, kwargs) #9.接收 timmer 函数的值 True,2,3
def inner(*args,**kwargs): #10.加载变量. 13.执行函数 inner
if flag: #14 flag = True
start_time = time.time() #15 获取当前时间
ret = f(*args,**kwargs) #16 执行 func1
time.sleep(0.3) #19 等待 0.3 秒
end_time = time.time() #20 获取当前时间
print('此函数的执行效率%f' % (end_time-start_time)) #21 打印差值
else:
ret = f(*args, **kwargs)
return ret #22 返回给函数调用者 func1()
return inner #11 返回给函数调用者 wrapper
return wrapper #7.返回给函数调用 timmer(flag,2,3)
flag = True #3 加载变量
@timmer(flag,2,3) # 4.执行函数 timmer(flag,2,3) 17.执行函数 func1 两步:1,timmer(flag,2,3) 相当于执行 wrapper 2.@wrapper 装饰器 func1 = wrapper(func1)
def func1(*args,**kwargs):
return 666 #18 返回给函数调用者 f(*args,**kwargs)
print(func1()) #12 执行函数
假定现在有 100 个函数,都加上了装饰器,增加了显示函数执行时间的功能,现在需要去掉!
怎能办?一行行代码去删除吗?太 low 了。
这个时候,直接在装饰器函数加一个参数即可。
import time
flag = True
def wrapper(f):
def inner(*args,**kwargs):
if flag:
start_time = time.time()
ret = f(*args,**kwargs)
time.sleep(0.3)
end_time = time.time()
print('此函数的执行效率%f' % (end_time-start_time))
else:
ret = f(*args, **kwargs)
return ret
return inner
@wrapper
def func1(*args,**kwargs):
# print(args,kwargs)
return 666
print(func1())
执行输出:
此函数的执行效率 0.300431
666
现在需要关闭显示执行时间
直接将 flag 改成 false
import time
flag = False
def wrapper(f):
def inner(*args,**kwargs):
if flag:
start_time = time.time()
ret = f(*args,**kwargs)
time.sleep(0.3)
end_time = time.time()
print('此函数的执行效率%f' % (end_time-start_time))
else:
ret = f(*args, **kwargs)
return ret
return inner
@wrapper
def func1(*args,**kwargs):
print(args,kwargs)
return 666
print(func1())
执行输出:
() {}
666
这样,所有调用的地方,就全部关闭了,非常方便
写装饰器,一般嵌套 3 层就可以了
a = 5
def func1():
a += 1
print(a)
func1()
执行报错
这里函数对全局变量做了改变,是不允许操作的。
函数内部可以引用全局变量,不能修改。如果要修改,必须要 global 一下
a = 5
def func1():
global a
a += 1
print(a)
func1()
执行输出 6
三、多个装饰器,装饰一个函数
def wrapper1(func): # func == f 函数名
def inner1():
print('wrapper1 ,before func') # 2
func()
print('wrapper1 ,after func') # 4
return inner1
def wrapper2(func): # func == inner1
def inner2():
print('wrapper2 ,before func') # 1
func()
print('wrapper2 ,after func') # 5
return inner2
@wrapper2 # f = wrapper2(f) 里面的 f==inner1 外面的 f == inner2
@wrapper1 # f = wrapper1(f) 里面的 f==函数名 f 外面的 f == inner1
def f(): # 3
print('in f')
f() # inner2()
执行输出:
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func
哪个离函数近,哪个先计算
最底下的先执行
执行顺序如下图:

多个装饰器,都是按照上图的顺序来的
今日作业:
1.写函数,返回一个扑克牌列表,里面有 52 项,每一项是一个元组
例如:[('红心',2),('草花',2), …('黑桃','A')]
2.写函数,传入 n 个数,返回字典{'max':最大值,'min':最小值}
例如:min_max(2,5,7,8,4)
返回:{'max':8,'min':2}
3.写函数,专门计算图形的面积
其中嵌套函数,计算圆的面积,正方形的面积和长方形的面积
调用函数 area('圆形',圆半径) 返回圆的面积
调用函数 area('正方形',边长) 返回正方形的面积
调用函数 area('长方形',长,宽) 返回长方形的面积
def area():
def 计算长方形面积():
pass
def 计算正方形面积():
pass
def 计算圆形面积():
pass
4.写函数,传入一个参数 n,返回 n 的阶乘
例如:cal(7)
计算 7*6*5*4*3*2*1
5、编写下载网页内容的函数,要求功能是:用户传入一个 url,函数返回下载页面的结果(升级题)
5.1.为题目 3 编写装饰器,实现缓存网页内容的功能:(升级题)
具体:实现下载的页面存放于文件中,如果网页有对应的缓存文件,就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
6 给每个函数写一个记录日志的功能,
功能要求:每一次调用函数之前,要将函数名称,时间节点记录到 log 的日志中。
所需模块:
import time
struct_time = time.localtime()
print(time.strftime("%Y-%m-%d %H:%M:%S",struct_time))
7、编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
8,在编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码。这个作业之上进行升级操作:
设置两套密码,一套为京东账号密码,一套为淘宝账号密码保存在文件中。
设置四个函数,分别代表 京东首页,京东超市,淘宝首页,淘宝超市。
循环打印四个选项:东首页,京东超市,淘宝首页,淘宝超市。
供用户选择,用户输入选项后,执行该函数,四个函数都加上认证功能,只要登陆成功一次,在选择其他函数,后续都无需输入用户名和密码。
相关提示:用带参数的装饰器。装饰器内部加入判断,验证不同的账户密码。
答案:
1、写函数,返回一个扑克牌列表,里面有 52 项,每一项是一个元组
例如:[(‘红心’,2),(‘草花’,2), …(‘黑桃’,’A’)]
1.1 准备基础数据
#颜色
colour = ['黑桃♠','红心♥','梅花♣','方块♦']
#牌面的值
card = list(range(2,11))+['A','J','Q','K']
1.2 使用 for 循环遍历
#颜色
colour = ['黑桃♠','红心♥','梅花♣','方块♦']
#牌面的值
card = list(range(2,11))+['A','J','Q','K']
for i in card:
for j in colour:
print((j,i))
执行输出:
(‘黑桃♠’, 2)
(‘红心♥’, 2)
(‘梅花♣’, 2)
…
1.3 封装成函数
#颜色
colour = ['黑桃♠','红心♥','梅花♣','方块♦']
#牌面的值
card = list(range(2,11))+['A','J','Q','K']
def poker(*args,**kwargs):
show_card = []
for i in kwargs['card']:
for j in kwargs['colour']:
show_card.append((j, i))
return show_card
print(poker(colour=colour,card=card))
执行输出:
[(‘黑桃♠’, 2), (‘红心♥’, 2), (‘梅花♣’, 2),…]
老师的代码:
def func(li):
l = []
for i in li:
#用 1~13 表示 13 张牌
for j in range(1,14):
l.append((i,j))
return l
print(func(['草花', '黑桃', '红桃', '方片']))
思考题,在上述代码的基础上修改一下
A 用 1 表示,2~10 表示数字牌,JQK 分别表示 11,12,13
代码如下:
def func(li):
l = []
for i in li:
#A 用 1 表示,2~10 表示数字牌,JQK 分别表示 11,12,13
for j in range(1,14):
if j == 1:
j = 'A'
elif j == 11:
j = 'J'
elif j == 12:
j = 'Q'
elif j == 13:
j = 'K'
l.append((i,j))
return l
print(func(['草花', '黑桃', '红桃', '方片']))
2、写函数,传入 n 个数,返回字典{‘max’:最大值,’min’:最小值}
例如:min_max(2,5,7,8,4)
返回:{‘max’:8,’min’:2}
2.1 使用内置函数,可以得出最大和最小值
a = (1,2,3)
b = {'k1':1,'k2':2}
print(max(a))
print(min(b.values()))
执行输出:
3
1
2.2 封装成函数
def min_max(*args,**kwargs):
dic = {'max':None,'min':None}
number = []
#循环位置变量
for i in args:
for j in i:
number.append(j)
# 循环关键字变量
for k in kwargs.values():
number.append(k)
#最大值和最小值
dic['max'] = max(number)
dic['min'] = min(number)
return dic
print(min_max([2,6,7,8,3,7,678,3,432,6547],a=1))
执行输出:
{‘min’: 1, ‘max’: 6547}
老师的代码:
def func2(*args):
return {'max':max(args), 'min':min(args)}
print(func2(1,2,3,4,5))
3、写函数,专门计算图形的面积
其中嵌套函数,计算圆的面积,正方形的面积和长方形的面积
调用函数 area(‘圆形’,圆半径) 返回圆的面积
调用函数 area(‘正方形’,边长) 返回正方形的面积
调用函数 area(‘长方形’,长,宽) 返回长方形的面积
def area():
def 计算长方形面积():
pass
def 计算正方形面积():
pass
def 计算圆形面积():
pass
先找出公式
长方形面积公式
S = ab
公式描述:公式中 a,b 分别为长方形的长和宽,S 为长方形的面积。
正方形面积公式
S = a²
公式描述:公式中 a 为正方形边长,S 为正方形面积。
圆的面积公式
S = πr²
公式描述:公式中 r 为圆的半径,π用3.14表示
写函数雏形
def area(*args,**kwargs):
#计算长方形面积
def rectangle(*args,**kwargs):
pass
#计算正方形面积
def square(*args,**kwargs):
pass
#计算圆形面积
def circular(*args,**kwargs):
pass
print(args)
ret = area('长方形', '长','宽')
print(ret)
执行输出:
(‘长方形’, ‘长’, ‘宽’)
None
填补函数
def area(*args,**kwargs):
#计算长方形面积
def rectangle(*args,**kwargs):
#print(args)
return args[0] * args[1]
#计算正方形面积
def square(*args,**kwargs):
return args[0] ** 2
#计算圆形面积
def circular(*args,**kwargs):
return 3.14 * (args[0] ** 2)
#判断参数
if args[0].strip() == '长方形':
return rectangle(args[1],args[2])
elif args[0].strip() == '正方形':
return square(args[1])
elif args[0].strip() == '圆形':
return circular(args[1])
else:
return '参数不正确!'
ret1 = area('长方形',3,4)
ret2 = area('正方形',5)
ret3 = area('圆形',6)
print(ret1)
print(ret2)
print(ret3)
执行输出:
12
25
113.04
老师的代码:
def area(*args):
#判断参数
if args[0] == '长方形':
def 计算长方形面积():
s = args[1]*args[2]
return s
return 计算长方形面积()
elif args[0] == '正方形':
def 计算正方形面积():
s = args[1] ** 2
return s
return 计算正方形面积()
elif args[0] == '圆形':
def 计算圆形面积():
s = 3.14 * (args[1] ** 2)
return s
return 计算圆形面积()
print(area('长方形',2,3))
print(area('正方形',5))
print(area('圆形',6))
4、写函数,传入一个参数 n,返回 n 的阶乘
例如:cal(7)
计算 7654321
4.1 先用 range 输出倒序的数字
range(开始,结束,步长)
默认步长为 1,如果为-1,表示倒序
for i in range(7,0,-1):
print(i)
执行输出:
7
6
5
4
3
2
1
4.2 封装函数
def cal(*args,**kwargs):
ret = 1
for i in range(args[0],0,-1):
ret *= i
return ret
print(cal(7))
执行输出:
5040
老师的代码:
def func3(n):
count = 1
for i in range(n,0,-1):
count = count * i
return count
print(func3(7))
5、编写下载网页内容的函数,要求功能是:用户传入一个 url,函数返回下载页面的结果(升级题)
5.1.为题目 3 编写装饰器,实现缓存网页内容的功能:(升级题)
具体:实现下载的页面存放于文件中,如果网页有对应的缓存文件,就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
import urllib.request
import os
import time
def download_index(*args, **kwargs):
flag = False
def inner():
if os.path.isfile('download.txt') == flag:
for i in args:
url = 'https://' + str(i)
response = urllib.request.urlopen(url).read().decode('utf-8')
time.sleep(1)
with open('download.txt', encoding='utf-8', mode='w') as f2:
f2.write(response)
return response
else:
with open('download.txt', encoding='utf-8', mode='r') as f3:
#print(inner.__closure__)
return f3.read()
return inner
print(download_index('www.baidu.com')())
执行输出: