python 全栈开发,Day12(函数的有用信息,带参数的装饰器,多个装饰器装饰一个函数)...
python 全栈开发,Day12(函数的有用信息,带参数的装饰器,多个装饰器装饰一个函数)
函数的执行时,*打散。
函数的定义时,*聚合。
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 retreturn 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 = func1def inner(*args,**kwargs): #聚合#args (1,2,3)'''执行函数之前的相关操作'''ret = f(*args,**kwargs) # 打散 1,2,3'''执行函数之后的相关操作'''return retreturn inner@wrapper
def func1():"""此函数是完成登陆的功能,参数分别是...作用。:return: 返回值是登陆成功与否(True,False)"""print(666)return Truefunc1()
print(func1.__name__)
print(func1.__doc__)
执行输出:
666
inner
执行函数之前的相关操作
咦?为什么输出了inner,我要的是func1啊。因为函数装饰之后,相当于执行了inner函数,所以输出inner
为了解决这个问题,需要调用一个模块wraps
wraps将 被修饰的函数(wrapped) 的一些属性值赋值给 修饰器函数(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 retreturn inner@wrapper
def func1():"""此函数是完成登陆的功能,参数分别是...作用。:return: 返回值是登陆成功与否(True,False)"""print(666)return Truefunc1()
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 retreturn innerreturn wrapperflag = True
@timmer(flag,2,3) # 两步:1,timmer(flag,2,3) 相当于执行wrapper 2.@wrapper 装饰器 func1 = wrapper(func1)
def func1(*args,**kwargs):return 666print(func1())
执行输出:
(True, 2, 3) {}
此函数的执行效率0.300183
666
代码分析
import time #1.加载模块
def timmer(*args,**kwargs): #2.加载变量 5.接收参数True,2,3def wrapper(f): #6.加载变量 8.f = func1print(args, kwargs) #9.接收timmer函数的值True,2,3def inner(*args,**kwargs): #10.加载变量. 13.执行函数innerif flag: #14 flag = Truestart_time = time.time() #15 获取当前时间ret = f(*args,**kwargs) #16 执行func1time.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 返回给函数调用者wrapperreturn 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 retreturn 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 retreturn inner@wrapper
def func1(*args,**kwargs):print(args,kwargs)return 666
print(func1())
执行输出:
() {}
666
这样,所有调用的地方,就全部关闭了,非常方便
写装饰器,一般嵌套3层就可以了
a = 5
def func1():a += 1print(a)func1()
执行报错
这里函数对全局变量做了改变,是不允许操作的。
函数内部可以引用全局变量,不能修改。如果要修改,必须要global一下
a = 5
def func1():global aa += 1print(a)func1()
执行输出6
三、多个装饰器,装饰一个函数
def wrapper1(func): # func == f函数名def inner1():print('wrapper1 ,before func') # 2func()print('wrapper1 ,after func') # 4return inner1def wrapper2(func): # func == inner1def inner2():print('wrapper2 ,before func') # 1func()print('wrapper2 ,after func') # 5return inner2@wrapper2 # f = wrapper2(f) 里面的f==inner1 外面的f == inner2
@wrapper1 # f = wrapper1(f) 里面的f==函数名f 外面的f == inner1
def f(): # 3print('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 计算长方形面积():passdef 计算正方形面积():passdef 计算圆形面积():pass4.写函数,传入一个参数n,返回n的阶乘
例如:cal(7)
计算7*6*5*4*3*2*15、编写下载网页内容的函数,要求功能是:用户传入一个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_cardprint(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,13for 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 dicprint(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):passprint(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 sreturn 计算长方形面积()elif args[0] == '正方形':def 计算正方形面积():s = args[1] ** 2return sreturn 计算正方形面积()elif args[0] == '圆形':def 计算圆形面积():s = 3.14 * (args[1] ** 2)return sreturn 计算圆形面积()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,表示倒序
for i in range(7,0,-1):print(i)
执行输出:
7
6
5
4
3
2
1
4.2封装函数
def cal(*args,**kwargs):ret = 1for i in range(args[0],0,-1):ret *= ireturn retprint(cal(7))
执行输出:
5040
老师的代码:
def func3(n):count = 1for i in range(n,0,-1):count = count * ireturn count
print(func3(7))
5、编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果(升级题)
5.1.为题目3编写装饰器,实现缓存网页内容的功能:(升级题)
具体:实现下载的页面存放于文件中,如果网页有对应的缓存文件,就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
import urllib.request
import os
import time
def download_index(*args, **kwargs):flag = Falsedef 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 responseelse:with open('download.txt', encoding='utf-8', mode='r') as f3:#print(inner.__closure__)return f3.read()return inner
print(download_index('www.baidu')())
执行输出:
<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准备示例函数
import time
def func1():"""此函数是测试的:return:"""print(666)time.sleep(0.3)return Truefunc1()
print(func1.__name__)
print(func1.__doc__)
执行输出:
666
func1
此函数是测试的
:return:
准备装饰器模板
def wrapper(f):def inner(*args,**kwargs):'''被装饰函数之前'''ret = f(*args,**kwargs)'''被装饰函数之后'''return retreturn inner
补充装饰器,完整代码如下:
import timedef 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 retreturn inner@wrapper
def func1():"""此函数是测试的:return:"""print(666)time.sleep(0.3)return Truefunc1()
执行输出:
666
函数名称:func1 时间节点:2018-04-02 16:25:05
增加写入日志功能
import timedef 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 retreturn inner@wrapper
def func1():"""此函数是测试的:return:"""print(666)time.sleep(0.3)return Truefunc1()
多执行几次程序,查看日志文件function_log.txt
老师的代码:
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 retreturn 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 函数
代码如下:
import time
dic = {'username':None,'status':False,
}
#错误次数
i = 0def wrapper(func):def inner(*args, **kwargs):# 判断登录状态是否为Trueif dic['status']:# 执行被装饰行函数ret = func(*args, **kwargs)return retelse:# 这里需要修改全局变量,要global一下global iwhile 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'] = usernamedic['status'] = Truestruct_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 retelse:print('账号或者密码错误,请重新输入%s机会' % (2 - i))i += 1'''函数执行之后操作'''#return retreturn inner@wrapper
def func1():time.sleep(0.4)print(555)@wrapper
def func2():time.sleep(0.5)print(666)func1()
func2()
执行输出:
查看日志
7、编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
7.1 准备函数雏形
def check_login(func): #检查登陆的装饰器def inner(*args,**kwargs):'''函数被装饰之前'''ret = func(*args,**kwargs)'''函数被装饰之后'''return retreturn innerdef 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")
填补代码,完整代码如下:
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 relse:print("\033[41;1m请先登录!\033[0m")#返回首页index()return inner#首页
def index():print("welcome to index page")global user_statuswhile 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# 当找到匹配时,跳出循环breakelse:result = False# 当整个用户列表遍历完成之后,再判断resultif result:#更改全局变量user_status['username'] = usernameuser_status['status'] = Truebreakelse: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'
#全局变量,用户状态
dic = {'username':None,'status':False,
}
#错误次数
i = 0def wrapper(func):def inner(*args, **kwargs):#判断登录状态是否为Trueif dic['status']:#执行被装饰行函数ret = func(*args, **kwargs)return retelse:#这里需要修改全局变量,要global一下global iwhile 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'] = usernamedic['status'] = Trueret = func(*args, **kwargs)return retelse:print('账号或者密码错误,请重新输入%s机会' % (2-i))i += 1return inner@wrapper
def article():print('文章')@wrapper
def diary():print('日记')@wrapper
def comment():print('评论')@wrapper
def file():print('文件')article()
diary()
comment()
file()
8,在编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码。这个作业之上进行升级操作:
设置两套密码,一套为京东账号密码,一套为淘宝账号密码保存在文件中。
设置四个函数,分别代表 京东首页,京东超市,淘宝首页,淘宝超市。
循环打印四个选项:东首页,京东超市,淘宝首页,淘宝超市。
供用户选择,用户输入选项后,执行该函数,四个函数都加上认证功能,只要登陆成功一次,在选择其他函数,后续都无需输入用户名和密码。
相关提示:用带参数的装饰器。装饰器内部加入判断,验证不同的账户密码。
不废话,直接贴代码
# -*- 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]}# 当找到匹配时,跳出循环breakelse:result = False#result = {'msg': False, 'level': li[2]}# 当整个用户列表遍历完成之后,再returnreturn resultdef check_login(func): #检查登陆的装饰器def inner(*args,**kwargs):if user_status['status']:r = func(*args,**kwargs)return relse:print("\033[41;1m请先登录!\033[0m")#返回首页login()func(*args, **kwargs)return innerdef login(): #登录程序global user_statuswhile True:print('请选择用户类型\n1.京东帐户\n2.淘宝账户\n')user_type = input('请输入编号: ').strip()if user_type == '1' or user_type == '2':breakelse: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_jduser_type = '京东帐户'else:file_name = file_taobaouser_type = '淘宝帐户'# 执行验证用户名和密码函数#print(file_name,username, password)result = username_password(file_name,username, password)if result:print('登陆成功!\n')user_status['username'] = usernameuser_status['status'] = Trueuser_status['user_type'] = user_typeindex()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()
执行输出:
老师的代码:
dic = {'username':None,'status':False,
}i = 0
def login(flag):def wrapper(func):def inner(*args, **kwargs):#判断用户状态是否为Trueif dic['status']:ret = func(*args, **kwargs)return retelse:global iwhile 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'] = usernamedic['status'] = True#执行被装饰的函数ret = func(*args, **kwargs)return retelse:print('您输入的用户或者密码错误,请重新输入,还有%s次机会' % (2-i))i += 1return innerreturn 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('您输入的有非法字符,请重新输入')
执行输出:
posted @ 2018-04-02 12:06 肖祥 阅读( ...) 评论( ...) 编辑 收藏
python 全栈开发,Day12(函数的有用信息,带参数的装饰器,多个装饰器装饰一个函数)...
python 全栈开发,Day12(函数的有用信息,带参数的装饰器,多个装饰器装饰一个函数)
函数的执行时,*打散。
函数的定义时,*聚合。
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 retreturn 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 = func1def inner(*args,**kwargs): #聚合#args (1,2,3)'''执行函数之前的相关操作'''ret = f(*args,**kwargs) # 打散 1,2,3'''执行函数之后的相关操作'''return retreturn inner@wrapper
def func1():"""此函数是完成登陆的功能,参数分别是...作用。:return: 返回值是登陆成功与否(True,False)"""print(666)return Truefunc1()
print(func1.__name__)
print(func1.__doc__)
执行输出:
666
inner
执行函数之前的相关操作
咦?为什么输出了inner,我要的是func1啊。因为函数装饰之后,相当于执行了inner函数,所以输出inner
为了解决这个问题,需要调用一个模块wraps
wraps将 被修饰的函数(wrapped) 的一些属性值赋值给 修饰器函数(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 retreturn inner@wrapper
def func1():"""此函数是完成登陆的功能,参数分别是...作用。:return: 返回值是登陆成功与否(True,False)"""print(666)return Truefunc1()
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 retreturn innerreturn wrapperflag = True
@timmer(flag,2,3) # 两步:1,timmer(flag,2,3) 相当于执行wrapper 2.@wrapper 装饰器 func1 = wrapper(func1)
def func1(*args,**kwargs):return 666print(func1())
执行输出:
(True, 2, 3) {}
此函数的执行效率0.300183
666
代码分析
import time #1.加载模块
def timmer(*args,**kwargs): #2.加载变量 5.接收参数True,2,3def wrapper(f): #6.加载变量 8.f = func1print(args, kwargs) #9.接收timmer函数的值True,2,3def inner(*args,**kwargs): #10.加载变量. 13.执行函数innerif flag: #14 flag = Truestart_time = time.time() #15 获取当前时间ret = f(*args,**kwargs) #16 执行func1time.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 返回给函数调用者wrapperreturn 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 retreturn 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 retreturn inner@wrapper
def func1(*args,**kwargs):print(args,kwargs)return 666
print(func1())
执行输出:
() {}
666
这样,所有调用的地方,就全部关闭了,非常方便
写装饰器,一般嵌套3层就可以了
a = 5
def func1():a += 1print(a)func1()
执行报错
这里函数对全局变量做了改变,是不允许操作的。
函数内部可以引用全局变量,不能修改。如果要修改,必须要global一下
a = 5
def func1():global aa += 1print(a)func1()
执行输出6
三、多个装饰器,装饰一个函数
def wrapper1(func): # func == f函数名def inner1():print('wrapper1 ,before func') # 2func()print('wrapper1 ,after func') # 4return inner1def wrapper2(func): # func == inner1def inner2():print('wrapper2 ,before func') # 1func()print('wrapper2 ,after func') # 5return inner2@wrapper2 # f = wrapper2(f) 里面的f==inner1 外面的f == inner2
@wrapper1 # f = wrapper1(f) 里面的f==函数名f 外面的f == inner1
def f(): # 3print('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 计算长方形面积():passdef 计算正方形面积():passdef 计算圆形面积():pass4.写函数,传入一个参数n,返回n的阶乘
例如:cal(7)
计算7*6*5*4*3*2*15、编写下载网页内容的函数,要求功能是:用户传入一个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_cardprint(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,13for 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 dicprint(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):passprint(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 sreturn 计算长方形面积()elif args[0] == '正方形':def 计算正方形面积():s = args[1] ** 2return sreturn 计算正方形面积()elif args[0] == '圆形':def 计算圆形面积():s = 3.14 * (args[1] ** 2)return sreturn 计算圆形面积()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,表示倒序
for i in range(7,0,-1):print(i)
执行输出:
7
6
5
4
3
2
1
4.2封装函数
def cal(*args,**kwargs):ret = 1for i in range(args[0],0,-1):ret *= ireturn retprint(cal(7))
执行输出:
5040
老师的代码:
def func3(n):count = 1for i in range(n,0,-1):count = count * ireturn count
print(func3(7))
5、编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果(升级题)
5.1.为题目3编写装饰器,实现缓存网页内容的功能:(升级题)
具体:实现下载的页面存放于文件中,如果网页有对应的缓存文件,就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
import urllib.request
import os
import time
def download_index(*args, **kwargs):flag = Falsedef 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 responseelse:with open('download.txt', encoding='utf-8', mode='r') as f3:#print(inner.__closure__)return f3.read()return inner
print(download_index('www.baidu')())
执行输出:
<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准备示例函数
import time
def func1():"""此函数是测试的:return:"""print(666)time.sleep(0.3)return Truefunc1()
print(func1.__name__)
print(func1.__doc__)
执行输出:
666
func1
此函数是测试的
:return:
准备装饰器模板
def wrapper(f):def inner(*args,**kwargs):'''被装饰函数之前'''ret = f(*args,**kwargs)'''被装饰函数之后'''return retreturn inner
补充装饰器,完整代码如下:
import timedef 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 retreturn inner@wrapper
def func1():"""此函数是测试的:return:"""print(666)time.sleep(0.3)return Truefunc1()
执行输出:
666
函数名称:func1 时间节点:2018-04-02 16:25:05
增加写入日志功能
import timedef 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 retreturn inner@wrapper
def func1():"""此函数是测试的:return:"""print(666)time.sleep(0.3)return Truefunc1()
多执行几次程序,查看日志文件function_log.txt
老师的代码:
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 retreturn 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 函数
代码如下:
import time
dic = {'username':None,'status':False,
}
#错误次数
i = 0def wrapper(func):def inner(*args, **kwargs):# 判断登录状态是否为Trueif dic['status']:# 执行被装饰行函数ret = func(*args, **kwargs)return retelse:# 这里需要修改全局变量,要global一下global iwhile 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'] = usernamedic['status'] = Truestruct_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 retelse:print('账号或者密码错误,请重新输入%s机会' % (2 - i))i += 1'''函数执行之后操作'''#return retreturn inner@wrapper
def func1():time.sleep(0.4)print(555)@wrapper
def func2():time.sleep(0.5)print(666)func1()
func2()
执行输出:
查看日志
7、编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
7.1 准备函数雏形
def check_login(func): #检查登陆的装饰器def inner(*args,**kwargs):'''函数被装饰之前'''ret = func(*args,**kwargs)'''函数被装饰之后'''return retreturn innerdef 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")
填补代码,完整代码如下:
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 relse:print("\033[41;1m请先登录!\033[0m")#返回首页index()return inner#首页
def index():print("welcome to index page")global user_statuswhile 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# 当找到匹配时,跳出循环breakelse:result = False# 当整个用户列表遍历完成之后,再判断resultif result:#更改全局变量user_status['username'] = usernameuser_status['status'] = Truebreakelse: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'
#全局变量,用户状态
dic = {'username':None,'status':False,
}
#错误次数
i = 0def wrapper(func):def inner(*args, **kwargs):#判断登录状态是否为Trueif dic['status']:#执行被装饰行函数ret = func(*args, **kwargs)return retelse:#这里需要修改全局变量,要global一下global iwhile 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'] = usernamedic['status'] = Trueret = func(*args, **kwargs)return retelse:print('账号或者密码错误,请重新输入%s机会' % (2-i))i += 1return inner@wrapper
def article():print('文章')@wrapper
def diary():print('日记')@wrapper
def comment():print('评论')@wrapper
def file():print('文件')article()
diary()
comment()
file()
8,在编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码。这个作业之上进行升级操作:
设置两套密码,一套为京东账号密码,一套为淘宝账号密码保存在文件中。
设置四个函数,分别代表 京东首页,京东超市,淘宝首页,淘宝超市。
循环打印四个选项:东首页,京东超市,淘宝首页,淘宝超市。
供用户选择,用户输入选项后,执行该函数,四个函数都加上认证功能,只要登陆成功一次,在选择其他函数,后续都无需输入用户名和密码。
相关提示:用带参数的装饰器。装饰器内部加入判断,验证不同的账户密码。
不废话,直接贴代码
# -*- 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]}# 当找到匹配时,跳出循环breakelse:result = False#result = {'msg': False, 'level': li[2]}# 当整个用户列表遍历完成之后,再returnreturn resultdef check_login(func): #检查登陆的装饰器def inner(*args,**kwargs):if user_status['status']:r = func(*args,**kwargs)return relse:print("\033[41;1m请先登录!\033[0m")#返回首页login()func(*args, **kwargs)return innerdef login(): #登录程序global user_statuswhile True:print('请选择用户类型\n1.京东帐户\n2.淘宝账户\n')user_type = input('请输入编号: ').strip()if user_type == '1' or user_type == '2':breakelse: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_jduser_type = '京东帐户'else:file_name = file_taobaouser_type = '淘宝帐户'# 执行验证用户名和密码函数#print(file_name,username, password)result = username_password(file_name,username, password)if result:print('登陆成功!\n')user_status['username'] = usernameuser_status['status'] = Trueuser_status['user_type'] = user_typeindex()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()
执行输出:
老师的代码:
dic = {'username':None,'status':False,
}i = 0
def login(flag):def wrapper(func):def inner(*args, **kwargs):#判断用户状态是否为Trueif dic['status']:ret = func(*args, **kwargs)return retelse:global iwhile 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'] = usernamedic['status'] = True#执行被装饰的函数ret = func(*args, **kwargs)return retelse:print('您输入的用户或者密码错误,请重新输入,还有%s次机会' % (2-i))i += 1return innerreturn 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('您输入的有非法字符,请重新输入')
执行输出:
posted @ 2018-04-02 12:06 肖祥 阅读( ...) 评论( ...) 编辑 收藏
发布评论