python 全栈开发,Day12(函数的有用信息,带参数的装饰器,多个装饰器装饰一个函数)
函数的执行时,*打散。
函数的定义时,*聚合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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___方法获取
举个例子
1 2 3 4 5 6 7 8 9 10 11 12 | 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)
这个有什么用呢?比如日志功能,需要打印出谁在什么时间,调用了什么函数,函数是干啥的,花费了多次时间,这个时候,就需要获取函数的有用信息了
带装饰器的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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将 被修饰的函数(wrapped) 的一些属性值赋值给 修饰器函数(wrapper) ,最终让属性的显示更符合我们的直觉
完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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)
二、带参数的装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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
代码分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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了。
这个时候,直接在装饰器函数加一个参数即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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层就可以了
1 2 3 4 5 6 | a = 5 def func1(): a + = 1 print (a) func1() |
执行报错
这里函数对全局变量做了改变,是不允许操作的。
函数内部可以引用全局变量,不能修改。如果要修改,必须要global一下
1 2 3 4 5 6 7 | a = 5 def func1(): global a a + = 1 print (a) func1() |
执行输出6
三、多个装饰器,装饰一个函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 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准备基础数据
1 2 3 4 | #颜色 colour = [ '黑桃♠' , '红心♥' , '梅花♣' , '方块♦' ] #牌面的值 card = list ( range ( 2 , 11 )) + [ 'A' , 'J' , 'Q' , 'K' ] |
1.2使用for循环遍历
1 2 3 4 5 6 7 8 | #颜色 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 封装成函数
1 2 3 4 5 6 7 8 9 10 11 12 13 | #颜色 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),...]
老师的代码:
1 2 3 4 5 6 7 8 | 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
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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使用内置函数,可以得出最大和最小值
1 2 3 4 | a = ( 1 , 2 , 3 ) b = { 'k1' : 1 , 'k2' : 2 } print ( max (a)) print ( min (b.values())) |
执行输出:
3
1
2.2封装成函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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}
老师的代码:
1 2 3 4 | 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表示
写函数雏形
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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
填补函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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
老师的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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)
计算7*6*5*4*3*2*1
4.1先用range输出倒序的数字
range(开始,结束,步长)
默认步长为1,如果为-1,表示倒序
1 2 | for i in range ( 7 , 0 , - 1 ): print (i) |
执行输出:
7
6
5
4
3
2
1
4.2封装函数
1 2 3 4 5 6 7 | def cal( * args, * * kwargs): ret = 1 for i in range (args[ 0 ], 0 , - 1 ): ret * = i return ret print (cal( 7 )) |
执行输出:
5040
老师的代码:
1 2 3 4 5 6 | 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编写装饰器,实现缓存网页内容的功能:(升级题)
具体:实现下载的页面存放于文件中,如果网页有对应的缓存文件,就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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' )()) |
执行输出:
<html>
<head>
<script>
...
</html>
6.给每个函数写一个记录日志的功能,
功能要求:每一次调用函数之前,要将函数名称,时间节点记录到log的日志中。
所需模块:
import time
struct_time = time.localtime()
print(time.strftime("%Y-%m-%d %H:%M:%S",struct_time))
6.1准备示例函数
1 2 3 4 5 6 7 8 9 10 11 12 13 | import time def func1(): """ 此函数是测试的 :return: """ print ( 666 ) time.sleep( 0.3 ) return True func1() print (func1.__name__) print (func1.__doc__) |
执行输出:
666
func1
此函数是测试的
:return:
准备装饰器模板
1 2 3 4 5 6 7 | def wrapper(f): def inner( * args, * * kwargs): '''被装饰函数之前''' ret = f( * args, * * kwargs) '''被装饰函数之后''' return ret return inner |
补充装饰器,完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import time def wrapper(f): def inner( * args, * * kwargs): '''被装饰函数之前''' ret = f( * args, * * kwargs) '''被装饰函数之后''' #标准时间 struct_time = time.localtime() standard_time = time.strftime( "%Y-%m-%d %H:%M:%S" ,struct_time) print ( '函数名称:{} 时间节点:{}\n' . format (f.__name__,standard_time)) return ret return inner @wrapper def func1(): """ 此函数是测试的 :return: """ print ( 666 ) time.sleep( 0.3 ) return True func1() |
执行输出:
666
函数名称:func1 时间节点:2018-04-02 16:25:05
增加写入日志功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | import time def wrapper(f): def inner( * args, * * kwargs): '''被装饰函数之前''' ret = f( * args, * * kwargs) '''被装饰函数之后''' #标准时间 struct_time = time.localtime() standard_time = time.strftime( "%Y-%m-%d %H:%M:%S" ,struct_time) #写入日志 with open ( 'function_log.txt' ,encoding = 'utf-8' ,mode = 'a+' ) as f1: f1.write( '函数名称:{} 时间节点:{}\n' . format (f.__name__,standard_time)) return ret return inner @wrapper def func1(): """ 此函数是测试的 :return: """ print ( 666 ) time.sleep( 0.3 ) return True func1() |
多执行几次程序,查看日志文件function_log.txt
老师的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | def wrapper(func): def inner( * args, * * kwargs): struct_time = time.localtime() time_now = time.strftime( "%Y-%m-%d %H:%M:%S" , struct_time) with open ( 'log' , encoding = 'utf-8' , mode = 'a' ) as f1: f1.write( '在时间是%s,执行了%s函数\n' % (time_now, func.__name__)) ret = func( * args, * * kwargs) '''函数执行之后操作''' return ret return inner @wrapper def func1(): time.sleep( 1 ) print ( 555 ) @wrapper def func2(): time.sleep( 2 ) print ( 666 ) func1() func2() |
思考题
执行func1和func2必须登录,才能执行。函数执行之后,记录日志
时间: xx年xx月xx日 xx 执行了 xx 函数
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | import time dic = { 'username' : None , 'status' : False , } #错误次数 i = 0 def wrapper(func): def inner( * args, * * kwargs): # 判断登录状态是否为True if dic[ 'status' ]: # 执行被装饰行函数 ret = func( * args, * * kwargs) return ret else : # 这里需要修改全局变量,要global一下 global i while i < 3 : username = input ( '请输入用户名:' ).strip() password = input ( '请输入密码:' ).strip() with open ( 'register_msg' , encoding = 'utf-8' ) as f1: for j in f1: j_li = j.strip().split() # ['张三','123'] if username = = j_li[ 0 ] and password = = j_li[ 1 ]: # 修改全局变量 dic[ 'username' ] = username dic[ 'status' ] = True struct_time = time.localtime() time_now = time.strftime( "%Y-%m-%d %H:%M:%S" , struct_time) with open ( 'log' , encoding = 'utf-8' , mode = 'a' ) as f1: f1.write( '时间:%s %s执行了 %s函数\n' % (time_now,username,func.__name__)) ret = func( * args, * * kwargs) return ret else : print ( '账号或者密码错误,请重新输入%s机会' % ( 2 - i)) i + = 1 '''函数执行之后操作''' #return ret return inner @wrapper def func1(): time.sleep( 0.4 ) print ( 555 ) @wrapper def func2(): time.sleep( 0.5 ) print ( 666 ) func1() func2() |
执行输出:
查看日志
7、编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
7.1 准备函数雏形
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | def check_login(func): #检查登陆的装饰器 def inner( * args, * * kwargs): '''函数被装饰之前''' ret = func( * args, * * kwargs) '''函数被装饰之后''' return ret return inner def index(): print ( "welcome to index page" ) @check_login def home(): #用户主页 print ( "welcome to home page" ) @check_login def bbs(): #bbs页面 print ( "welcome to bbs page" ) |
填补代码,完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | import os #文件名 file_name = 'user_list.txt' #用户状态 user_status = { 'username' : None , 'status' : False } def check_login(func): #检查登陆的装饰器 def inner( * args, * * kwargs): #print(user_status['status']) if user_status[ 'status' ]: r = func( * args, * * kwargs) return r else : print ( "\033[41;1m请先登录!\033[0m" ) #返回首页 index() return inner #首页 def index(): print ( "welcome to index page" ) global user_status while True : username = input ( "username:" ).strip() password = input ( "password:" ).strip() # 判断文件是否存在 if os.path.exists(file_name) = = False : with open (file_name, encoding = 'utf-8' , mode = 'w' ) as mk: mk.write( 'xiao 123' ) with open (file_name, encoding = 'utf-8' , mode = 'r' ) as f1: for i in f1: # 去空格,以空格切割,转换为列表 li = i.strip().split() # [张三,123] # 判断用户名和密码是否匹配 if username = = li[ 0 ] and password = = li[ 1 ]: result = True # 当找到匹配时,跳出循环 break else : result = False # 当整个用户列表遍历完成之后,再判断result if result: #更改全局变量 user_status[ 'username' ] = username user_status[ 'status' ] = True break else : print ( '用户名和密码不正确,请重新输入' ) @check_login def home(): #用户主页 print ( "welcome to home page" ) @check_login def bbs(): #bbs页面 print ( "welcome to bbs page" ) index() home() bbs() |
执行输出:
老师的代码:
必须提前创建好文件register_msg,默认内容为'张三 123'
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | #全局变量,用户状态 dic = { 'username' : None , 'status' : False , } #错误次数 i = 0 def wrapper(func): def inner( * args, * * kwargs): #判断登录状态是否为True if dic[ 'status' ]: #执行被装饰行函数 ret = func( * args, * * kwargs) return ret else : #这里需要修改全局变量,要global一下 global i while i < 3 : username = input ( '请输入用户名:' ).strip() password = input ( '请输入密码:' ).strip() with open ( 'register_msg' ,encoding = 'utf-8' ) as f1: for j in f1: j_li = j.strip().split() # ['张三','123'] if username = = j_li[ 0 ] and password = = j_li[ 1 ]: #修改全局变量 dic[ 'username' ] = username dic[ 'status' ] = True ret = func( * args, * * kwargs) return ret else : print ( '账号或者密码错误,请重新输入%s机会' % ( 2 - i)) i + = 1 return inner @wrapper def article(): print ( '文章' ) @wrapper def diary(): print ( '日记' ) @wrapper def comment(): print ( '评论' ) @wrapper def file (): print ( '文件' ) article() diary() comment() file () |
8,在编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码。这个作业之上进行升级操作:
设置两套密码,一套为京东账号密码,一套为淘宝账号密码保存在文件中。
设置四个函数,分别代表 京东首页,京东超市,淘宝首页,淘宝超市。
循环打印四个选项:东首页,京东超市,淘宝首页,淘宝超市。
供用户选择,用户输入选项后,执行该函数,四个函数都加上认证功能,只要登陆成功一次,在选择其他函数,后续都无需输入用户名和密码。
相关提示:用带参数的装饰器。装饰器内部加入判断,验证不同的账户密码。
不废话,直接贴代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | # -*- coding: utf-8 -*- import os #文件名 file_jd = 'jd.txt' file_taobao = 'taobao.txt' #用户状态 user_status = { 'username' : None , 'status' : False , 'user_type' : None } def username_password(file_name,username,password): ''' #判断用户名和密码是否匹配 :param username: 用户名 :param password: 密码 :return: True 匹配成功 False 匹配失败 ''' if username = = ' ' or password == ' ': return '用户名或者密码不能为空!' with open (file_name, encoding = 'utf-8' , mode = 'r' ) as f3: for i in f3: # print(i) # 去空格,以空格切割,转换为列表 li = i.strip().split() # [张三,123] # 判断用户名和密码是否匹配 if username = = li[ 0 ] and password = = li[ 1 ]: result = True #result = {'msg':True,'level':li[2]} # 当找到匹配时,跳出循环 break else : result = False #result = {'msg': False, 'level': li[2]} # 当整个用户列表遍历完成之后,再return return result def check_login(func): #检查登陆的装饰器 def inner( * args, * * kwargs): if user_status[ 'status' ]: r = func( * args, * * kwargs) return r else : print ( "\033[41;1m请先登录!\033[0m" ) #返回首页 login() func( * args, * * kwargs) return inner def login(): #登录程序 global user_status while True : print ( '请选择用户类型\n1.京东帐户\n2.淘宝账户\n' ) user_type = input ( '请输入编号: ' ).strip() if user_type = = '1' or user_type = = '2' : break else : print ( "\033[41;1m输入错误,请重新输入!\033[0m" ) while True : username = input ( '请输入用户名,或输入q返回主页:' ).strip() if username.upper() = = 'Q' : index() if username = = '': print ( "\033[41;1m用户名不能为空!\033[0m" ) password = input ( '请输入密码:' ).strip() if password = = '': print ( "\033[41;1m密码不能为空!\033[0m" ) if username ! = ' ' and password != ' ': #break # 判断文件是否存在 if os.path.exists(file_jd) = = False or os.path.exists(file_taobao) = = False : with open (file_jd, encoding = 'utf-8' , mode = 'w' ) as f_jd: f_jd.write( 'xiao 123' ) with open (file_taobao, encoding = 'utf-8' , mode = 'w' ) as f_taobao: f_taobao.write( 'tao 123' ) if user_type = = '1' : file_name = file_jd user_type = '京东帐户' else : file_name = file_taobao user_type = '淘宝帐户' # 执行验证用户名和密码函数 #print(file_name,username, password) result = username_password(file_name,username, password) if result: print ( '登陆成功!\n' ) user_status[ 'username' ] = username user_status[ 'status' ] = True user_status[ 'user_type' ] = user_type index() else : print ( "\033[41;1m用户名或密码错误!\033[0m" ) #首页 def index(): menu = [ '京东首页' , '京东超市' , '淘宝首页' , '淘宝超市' , '退出' ] #菜单函数名对照表 menu_func = [ 'jd_index' , 'jd_supermarket' , 'taobao_index' , 'taobao_supermarket' , 'exit' ] print ( "꧁欢迎访问购物商城꧂" .center( 30 , '❀' )) status = '在线' if user_status[ 'status' ] = = True else '未登录' info = '\n当前登陆用户:{} 状态: {} 用户类型:{}\n' . format (user_status[ 'username' ], status, user_status[ 'user_type' ]) print (info.rjust( 15 )) for i in range ( len (menu)): print ( '{}\t{}' . format (i + 1 , menu[i])) print ("".center( 35 , '☪' )) while True : number = input ( "请输入序号:" ).strip() if number.isdigit(): number = int (number) if number > 0 and number < = len (menu): #执行菜单函数 eval (menu_func[number - 1 ])() else : print ( '输入错误,请重新输入!' ) @check_login def jd_index(): #京东首页 #print("欢迎访问 京东首页") print ( "\033[32m欢迎访问 京东首页\033[0m" ) @check_login def jd_supermarket(): #京东超市 #print("欢迎访问 京东超市") print ( "\033[32m欢迎访问 京东超市\033[0m" ) @check_login def taobao_index(): #淘宝首页 #print("欢迎访问 淘宝首页") print ( "\033[32m欢迎访问 淘宝首页\033[0m" ) @check_login def taobao_supermarket(): #淘宝超市 #print("欢迎访问 淘宝超市") print ( "\033[32m欢迎访问 淘宝超市\033[0m" ) #执行函数 if __name__ = = '__main__' : index() |
执行输出:
老师的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | dic = { 'username' : None , 'status' : False , } i = 0 def login(flag): def wrapper(func): def inner( * args, * * kwargs): #判断用户状态是否为True if dic[ 'status' ]: ret = func( * args, * * kwargs) return ret else : global i while i < 3 : username = input ( '请输入用户名(用%s账号):' % flag).strip() password = input ( '请输入密码:' ).strip() with open ( 'user_pwd' ,encoding = 'utf-8' ) as f1: #读取一行,因为文件只有一行内容 msg_dic = eval (f1.readline()) # {'微信': {'password': '123', 'username': '老男孩'}, 'qq': {'password': '123', 'username': '老男孩1'}} if username = = msg_dic[flag][ 'username' ] and password = = msg_dic[flag][ 'password' ]: #修改全局变量,这里不用global,因为它是可变类型 dic[ 'username' ] = username dic[ 'status' ] = True #执行被装饰的函数 ret = func( * args, * * kwargs) return ret else : print ( '您输入的用户或者密码错误,请重新输入,还有%s次机会' % ( 2 - i)) i + = 1 return inner return wrapper @login ( '微信' ) def taobao_home(): print ( '淘宝首页' ) @login ( '微信' ) def taobao_shop(): print ( '淘宝超市' ) @login ( 'qq' ) def jingdong_home(): print ( '京东首页' ) @login ( 'qq' ) def jingdong_shop(): print ( '京东超市' ) #菜单列表,建议使用此方式 choice_dict = { #序号:函数名 1 : taobao_home, 2 : taobao_shop, 3 : jingdong_home, 4 : jingdong_shop, 5 : exit, } while True : print ( '1 淘宝首页\n2 淘宝超市\n3 京东首页\n4 京东超市\n5 退出' ) choice_num = input ( '请选择输入的序号:' ).strip() if choice_num.isdigit(): choice_num = int (choice_num) #判断数字范围 if 0 < choice_num < = len (choice_dict): #执行函数 choice_dict[choice_num]() else : print ( '请输入范围内的序号' ) else : print ( '您输入的有非法字符,请重新输入' ) |
执行输出:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix