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('您输入的有非法字符,请重新输入')

执行输出:

 

 

  

posted @   肖祥  阅读(914)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· 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
点击右上角即可分享
微信分享提示