玩了一晚上王者,突然觉得该学习,然后大晚上的搞出来这道练习题,凌晨一点写博客(之所以这么晚就赶忙写是因为怕第二天看自己程序都忘了咋写的了),我太难了o(╥﹏╥)o
言归正传,练习题要求:构造类似京东的一个网站首页(超级简化),实现函数装饰器的设计(主要设计),装饰的内容为,无论添加什么商品进购物车,最终付款的时候都需要登录才能支付,且无论在哪个页面下登录过一次都不需要再次登录,即做一个函数装饰器,实现登录功能(装饰到所有商品界面)。
我的实现:商品分类页面进行函数话,即一个类别构造一个函数,选择商品类别即执行函数,至于函数的内容就是简单的打印下商品名称,毕竟重点在登录,商品类别函数内具体有什么不重要。当第一次登录时,可选择登录方式:京东或者微信,在项目文件夹中创建了两个txt文件,里面没啥东西,就一个TXT存一个字典,字典里面预设两个key当做用户名及密码。登录一次后再运行程序(我给设成自动的了),即选择其他商品类别,将不会再让登录验证。
具体实现效果图:
具体代码:
login_type = "jingdong"#用于初始化带参装饰器,然而我没用上
juge_login = False#用于判断登录状态,直接全局 def login(type):#装饰器为两层def,带参装饰器为三层def,但说实话,我的这个带参装饰器其实并没有在这个程序中起到什么作用,但提供了一个可用参数,我没用到,但这个参数是可以用的,可以做个判断加个功能啥的
def auth_method(fuction):
def really_login():
global juge_login
if(juge_login == False):
login_type = input('''请选择登录的方式:
1、京东
2、微信
''')
if(login_type == "1"):login_type = "jingdong"
if(login_type == "2"):login_type = "weixin"
username = input("请输入你的用户名:")
password = input("请输入你的密码:")
f = open(login_type,"r")#f.readline()返回的是一行,类型为str;f.readlines()返回的是所有行,返回类型是list(一行一个元素,因为我存了两个键用了一行,所以直接取第一个元素)
key = eval(f.readlines()[0])#虽然我存储的是字典形式的文本,但提取出来后只是长得跟字典一模一样的字符串,eval的功能就是将长得一模一样的str转为dict
for m,n in key.items():#遍历查找文本所有账户寻找匹配项
if(username == m and password == n):
print("登陆成功")
juge_login = True#置位 f.close()
if(juge_login == False):
print("用户名或密码错误,请重新输入:")
return really_login() fuction()#当登录完成或者登录过一次后,就不会执行上面的if判断(if判断含return,如果登不成功永远停留在登录上而无法执行下一步)
print("程序尚未完成,请选择继续测试功能:")
Start_main()#为了测试而专门放在这循环的,这个不是装饰的一部分,讲道理登录的装饰完就完了,这个可以加在main里搞个循环啥的,太懒了,就这样吧
return really_login#注意,返回的是函数(函数的内存地址,后面加上()是可以执行返回函数的,而不是结果(类似str,int等类型结果))
return auth_method @login(login_type)#@login() == @auth_method
def jiaju():
print('''请选择你要购买的商品:
1、海尔电冰箱
2、格力空调
3、席梦思床垫''') @login(login_type)#带参装饰,被装饰函数为@的下一行定义函数,用于装饰的函数为@后面紧跟的函数(带参函数略有不同)
def shipin():
print('''请选择你要购买的商品:
1、盼盼鸡味块
2、乐事薯片
3、伊利安慕希''') @login(login_type)
def yiwu():
print('''请选择你要购买的商品:
1、耐克球鞋
2、波司登棉袄
3、阿迪达斯帽子''') def Start_main():
print('''欢迎来到京东购物商城:
1、家具
2、食品
3、衣物''')
choose = input("请选择你要购买的类别:")
if(choose == "1"):
jiaju()
elif(choose == "2"):
shipin()
elif(choose == "3"):
yiwu()
else:print("输入错误,请输入商品类别前面的数字代码。") if __name__ == '__main__':#主函数调用开始界面函数(首页)
Start_main() 解析:装饰函数最大的特点在于返回值,返回的是函数本题(函数内存地址,相当于变量,指向的一块地址),而不是函数执行过后的结果,比如
def add(a,b):
return a+b
add与add(1,2)的区别在于什么?不是1,2带不带参数的区别,而是add是一个“变量”,指向一个内存地址,当访问(执行)这块地址时(执行方法为地址+());而add(1,2)是执行后返回的结果(无返回值为None),是动态的,会自己运行的。所以当return add时是返回add的内存地址,返回add()是返回add执行的结果,前者在返回值后面加上()就可以再次作为函数执行,后者是结果,加上()会报错,就像字符串后面加()
一样,无意义。
装饰函数就是利用了上述的返回内存地址这一特点产生的:
def decorat(f):
def inner(f):
return abs(f())
return inner
def add(x,y): 其中这三行等同于:@decorat
return x+y def add(x,y):
add = decorat(add) return x+y
上面的装饰器完成加完后取绝对值(写程序时有点迷装饰器卡了下看了篇文章他就是用这个例子,我借用下),装饰器的意义在于,不修改add这个函数,但却给add做了调整(装饰,给add加了些功能),关键点在于内层既执行了add(目标函数)又有其他的语句,然后又将内层函数的地址传给目标函数的“变量“,毕竟函数的名称是什么不重要,重要的是名称背后的地址。至于带参装饰函数更简单了,没有想象的那么好用或者说神奇,@XX这种形式的装饰明白的话,你就想,能不能在@XX后面加括号做手脚带个参数啥的,答案是可以,但带了参数就相当于执行函数了,即@和XX()是分开的东西,你的所要达到的效果是让XX()执行的结果等于函数装饰器的外层函数的内存地址,即XX()的返回值为第二层函数地址本身,明白这一点,则带参装饰器包括装饰器都明白了。
都两点了,赶紧睡觉,我太难了o(╥﹏╥)o