python3--函数的有用信息,带参数的装饰器,多个装饰器装饰同一个函数

发布时间:2018-04-03 20:35:01编辑:Run阅读(3731)

    开放封闭原则

    1 对扩展是开放的

        为什么要对扩展开放呢?

        我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改,所以我们必须允许代码扩展,添加新功能

    2 对修改是封闭的

        为什么要对修改封闭呢?

        就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经在使用该函数的用户


    函数的有用信息

    def func1():
        """
        此函数打印函数名与注释
        :return:返回值为True
        """
        print(666)
        print(func1.__name__) # func1.__name__ 函数名
        print(func1.__doc__)  # funcl.__doc__ 注释信息
        return True
    
    func1()
    print(func1())

    执行结果

    blob.png


    例2

    使用装饰器打印出函数的相关信息

    from functools import wraps
    def deco(f):  
        @wraps(f)  # 加在最内层函数正上方
        def wrapper(*args, **kwargs):
            return f(*args, **kwargs)
        return wrapper
    
    @deco # test = deco(test)
    def test():
        '''测试'''
        print('from test')
    test()
    print(test.__name__)  # 打印函数名
    print(test.__doc__)   # 打印注释信息

    执行结果

    from test

    test

    测试


    带参数的装饰器

    import time
    flag = False  #标志位
    def timer(flag):
        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("次函数的执行效率{}".format(end_time - start_time))
                else:
                    ret = f(*args, **kwargs)
                return ret
            return inner
        return wrapper
    
    flag = True
    @timer(flag)
    def func1():
        print(666)
    @timer(flag)
    def func2():
        print(777)
    
    func1()
    func2()

    执行结果

    blob.png


    多个装饰器装饰一个函数

    def warpper1(func):
        def inner1(*args, **kwargs):
            print("wrapper1, before func")  # 2
            func(*args, **kwargs)
            print("wrapper1, after func")   # 4
        return inner1
    
    def wrapper2(func):
        def inner2(*args, **kwargs):
            print('wrapper2, before func')  # 1
            func(*args, **kwargs)
            print('wrapper2, after func')   # 5
        return inner2
    
    @wrapper2  # f = wrapper2(f) 里面的f=inner1 外面的f == inner2
    @warpper1  # f = wrapperl(f) 最里面的f==函数名f,外面的f==inner1
    def f():
        print('in f')   # 3
    f()

    执行结果

    blob.png


    练习题

    # !/usr/bin/env python
    # coding: utf-8
    __author__ = 'www.py3study.com'
    1.写函数,返回一个扑克牌列表,里面有52项,每一项是一个元组
    例如:[(‘红心’,2), (‘草花’,2), …(‘黑桃’,‘A’)]
    list1 = ['黑桃♠', '红心♥', '梅花♣', '方块♦']
    list2 = ['A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K']
    
    1 位置传参
    def poker(*args, **kwargs):
        list3 = []
        for x in args[0]:
            for y in args[1]:
                list3.append((x, y))
        return list3
    print(poker(list1, list2))
    
    2 关键字传参
    def poker(*args, **kwargs):
        list4 = []
        for x in kwargs['test1']:
           for y in kwargs['test2']:
               list4.append((x, y))
        return list4
    print(poker(test1=list1, test2=list2))
    
    3 for range
    def func(li):
        l = []
        for i in li:
            for j in range(1, 14):
                l.append((i, j))
        return l
    print(func(['黑桃♠', '红心♥', '梅花♣', '方块♦']))
    
    2.
    写函数,传入n个数,返回字典
    {‘max’:最大值,’min’:最小值}
    例如: min_max(2, 5, 7, 8, 4)
    返回: {‘max’:8,’min’:2}
    def compare(*args, **kwargs):
        return {'max': max(args), 'min': min(args)}
    print(compare(12, 324, 54,1,3,4,10,5,6,7))
    
    3.
    写函数,专门计算图形的面积
    其中嵌套函数,计算圆的面积,正方形的面积和长方形的面积
    调用函数area(‘圆形’, 圆半径)  返回圆的面积
    调用函数area(‘正方形’, 边长)  返回正方形的面积
    调用函数area(‘长方形’, 长,宽)  返回长方形的面积
    
    def area(*args, **kwargs):
        if args[0] == '圆形':
            def rectangle_area():
                s1 = 3.14 * args[1]
                return s1
            return rectangle_area()
        elif args[0] == '正方形':
            def square_area():
                s2 = args[1] * args[1]
                return s2
            return square_area()
        else:
            def circular_area():
                s3 = args[1] * args[2]
                return s3
            return circular_area()
    print(area("圆形", 6))
    print(area("正方形", 5))
    print(area("长方形", 2, 3))
    
    
    
    4.
    写函数,传入一个参数n,返回n的阶乘
    例如: cal(7)
    计算7 * 6 * 5 * 4 * 3 * 2 * 1
    
    1 递归求解,设置一个出口
    def factorial(n):
        if n == 1:
            return 1
        return n * factorial(n - 1)
    print(factorial(7))
    
    2 for range利用*=求解
    def func(n):
        count = 1
        for i in range(n,0,-1):
            count *= i
        return count
    print(func(7))
    
    5、编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果(升级题)
    5.1.为题目3编写装饰器,实现缓存网页内容的功能:(升级题)
    具体:实现下载的页面存放于文件中,如果网页有对应的缓存文件,就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
    import urllib.request
    import os
    import time
    # def download_index(*args, **kwargs):
    #     for i in args:
    #         url = 'https://' + str(i)
    #         response = urllib.request.urlopen(url).read().decode('utf-8')
    #         with open('download.txt', encoding='utf-8', mode='w') as f2:
    #             f2.write(response)
    #         return response
    # print(download_index('www.baidu.com'))
    
    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)
                    req = urllib.request.Request(url)
                    req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0')
                    response = urllib.request.urlopen(req).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')())
    
    6
    给每个函数写一个记录日志的功能,
    功能要求:每一次调用函数之前,要将函数名称,时间节点记录到log的日志中。
    所需模块:
    import time
    from functools import wraps
    def timer(f):
        @wraps(f)
        def inner(*args, **kwargs):
            struct_time = time.localtime()
            ss = time.strftime("%Y-%m-%d %H:%M:%S", struct_time)
            with open("log.txt", encoding='utf-8', mode='a') as f1:
                f1.write("函数名:{}\t函数描述:{}\t运行时间:{}\n".format(f.__name__, f.__doc__, ss))
            ret = f(*args, **kwargs)
            '''函数执行之后'''
            return ret
        return inner
    
    @timer
    def test1():
        '''this is test1'''
        print('in test1')
    @timer
    def test2():
        '''this is test2'''
        time.sleep(3)
        print('in test2')
    test1()
    test2()
    
    7、编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
    方法1
    flag = False
    dic = {}
    def test():
        while True:
            username = input("输入账户名:").strip()
            if not username.strip():
                print('账号不能为空')
            else:
                password = input("输入密码:").strip()
                if not password.strip():
                    print('密码不能为空')
                else:
                    with open('user_pwd', encoding='utf-8', mode='r') as fq:
                        for i in fq:
                            s = i.strip().split()
                            dic[s[0]] = s[1]
                    for i in dic.items():
                        if username == i[0] and password == i[1]:
                            global flag
                            flag = True
                            break
                        else:
                            flag = False
                    if flag:
                        print("登录成功")
                        return True
                    else:
                        print("账号或密码错误!")
    
    def wrapper(f):
        def inner(*args, **kwargs):
            if flag:
                f(*args, **kwargs)
                return f
            else:
                print("请先登录")
                test()
                f(*args, **kwargs)
        return inner
    
    @wrapper
    def test1():
        print("this is test1")
    @wrapper
    def test2():
        print("this is test2")
    
    test1()
    test2()
    
    方法2
    dic = {
        'username': None,
        'status': False,
    }
    def wrapper(func):
        def inner(*args, **kwargs):
            if dic['status']:
                ret = func(*args, **kwargs)
                return ret
            else:
                i = 0
                while i < 3:
                    username = input("请输入用户名:").strip()
                    password = input("请输入密码:").strip()
                    with open('user_pwd', encoding='utf-8', mode='r') as f:
                        for j in f:
                            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('账号或密码错误,请重新输入{}机会'.format(2 - i))
                            i += 1
        return inner
    
    @wrapper
    def article():
        print('欧美专区......')
    @wrapper
    def diary():
        print('日韩专区......')
    @wrapper
    def file():
        print('泰国专区......')
    @wrapper
    def comment():
        print('北美专区......')
    article()
    diary()
    file()
    comment()
    
    8,在编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码。这个作业之上进行升级操作:
    设置两套密码,一套为京东账号密码,一套为淘宝账号密码保存在文件中。
    设置四个函数,分别代表
    京东首页,京东超市,淘宝首页,淘宝超市。
    循环打印四个选项:东首页,京东超市,淘宝首页,淘宝超市。
    供用户选择,用户输入选项后,执行该函数,四个函数都加上认证功能,只要登陆成功一次,在选择其他函数,后续都无需输入用户名和密码。
    相关提示:用带参数的装饰器。装饰器内部加入判断,验证不同的账户密码。
    
    方法1
    import time
    import platform
    import os
    
    class Shopping(object):
        '''初始化'''
        def __init__(self, *args, **kwargs):
            self.timeout = 1
            self.flag = False
            self.sign = False
            self.jd_dic = {}
            self.tb_dic = {}
            self.run = {
                '0': self.Error,
                '1': self.jd_home_page,
                '2': self.jd_supermarket,
                '3': self.taobao_home_page,
                '4': self.taobao_supermarket,
            }
            self.main()
    
        '''装饰器'''
    
        def app(func):
            def inner(self):
                if self.sign:
                    func(self)
                    return func
                else:
                    print("|| 请先登录!!!!")
                    self.jd_or_tb()
                    func(self)
    
            return inner
    
        '''程序入口'''
        def main(self, *args, **kwargs):
            while True:
                self.menu()
                op = input(u'|| 输入选项:').strip()
                '''map判断输入是否符合条件'''
                if op == '0':
                    exit()
                elif op in map(str, range(len(self.run))):
                    self.run.get(op)()
                else:
                    self.error()
                    continue
    
        def menu(self, *args, **kwargs):
            self.clear()
            self.create_text()
            print(u'=' * 40)
            print(u'|| 0:退出程序')
            print(u'|| 1:京东首页')
            print(u'|| 2:京东超市')
            print(u'|| 3:淘宝首页')
            print(u'|| 4:淘宝超市')
            print(u'=' * 42)
    
        def clear(self, *args, **kwargs):
            OS = platform.system()
            if (OS == u'Windows'):
                os.system('cls')
            else:
                os.system('clear')
    
    
        '''创建密码文件'''
        def create_text(self, *args, **kwargs):
            if os.path.isfile('jd_name_pwd.txt') == self.flag:
                with open("jd_name_pwd.txt", encoding='utf-8', mode='w') as f:
                    f.write('zhangsan' + ' ' + '123')
            else:
                with open('jd_name_pwd.txt', encoding='utf-8', mode='r') as f1:
                    for i in f1:
                        ss = i.strip().split()
                        self.jd_dic['name'] = ss[0]
                        self.jd_dic['pwd'] = ss[1]
    
            if os.path.isfile('taobao_name_pwd.txt') == self.flag:
                with open('taobao_name_pwd.txt', encoding='utf-8', mode='w') as f2:
                    f2.write('lisi' + ' ' + '123')
            else:
                with open('taobao_name_pwd.txt', encoding='utf-8', mode='r') as f3:
                    for i in f3:
                        ss = i.strip().split()
                        self.tb_dic['name'] = ss[0]
                        self.tb_dic['pwd'] = ss[1]
    
        def jd_or_tb(self,*args, **kwargs):
            while True:
                print("|| 1:京东账号:")
                print("|| 2:淘宝账号:")
                self.list_jd_or_tb = {
                    '1': self.create_text,
                    '2': self.create_text,
                }
                jd_tb = input(u'|| 输入选项:').strip()
                '''map判断输入是否符合条件'''
                if jd_tb == '0':
                    return True
                elif jd_tb in map(str, range(len(self.list_jd_or_tb) + 1)):
                    self.run.get(self.sign_in())
                else:
                    self.Error()
                    continue
    
    
        def sign_in(self, *args, **kwargs):
            while True:
                username = input('|| 请输入账号:').strip()
                if not username.strip():
                    print("|| 账号名不能为空!")
                else:
                    password = input('|| 请输入密码:').strip()
                    if not password.strip():
                        print("|| 密码不能为空!")
                    else:
                        if username == self.jd_dic['name'] and password == self.jd_dic['pwd']:
                            print('|| 京东账号登陆成功')
                            self.sign = True
                            self.main()
                        elif username == self.tb_dic['name'] and password == self.tb_dic['pwd']:
                            print('|| 淘宝账号登陆成功')
                            self.sign = True
                            self.main()
                        else:
                            print('账号名或密码错误')
    
    
        @app
        def jd_home_page(self, *args, **kwargs):
            print(u'|| 京东-----首页')
    
        @app
        def jd_supermarket(self, *args, **kwargs):
            print(u'|| 京东-----超市')
    
        @app
        def taobao_home_page(self, *args, **kwargs):
            print(u'|| 淘宝-----首页')
    
        @app
        def taobao_supermarket(self, *args, **kwargs):
            print(u'|| 淘宝-----超市')
    
        def Error(self, *args, **kwargs):
            print(u'|| 只能输入1-2的整数,等待{}秒后重新输入'.format(self.timeout))
            time.sleep(self.timeout)
    
        def error(self, *args, **kwargs):
            print(u'|| 只能输入1-4的整数,等待{}秒后重新输入'.format(self.timeout))
            time.sleep(self.timeout)
    if __name__ == '__main__':
        Shopping()
    
    方法2
    dic = {
        'username': None,
        'status': False,
    }
    def login(flag):
        def wrapper(func):
            def inner(*args, **kwargs):
                if dic['status']:
                    ret = func(*args, **kwargs)
                    return ret
                else:
                    i = 0
                    while i < 3:
                        username = input('请输入用户名:').strip()
                        password = input('请输入密码:').strip()
                        with open('jd_tb', encoding='utf-8') as f:
                            # {'微信': {'username': 'zhangsan', 'password': '123'}, 'qq': {'username': 'lisi', 'password': '123'}, }
                            msg_dic = eval(f.readline())
                            if username == msg_dic[flag]['username'] and password == msg_dic[flag]['password']:
                                dic['username'] = username
                                dic['status'] = True
                                ret = func(*args, **kwargs)
                                return ret
                            else:
                                print("账号或密码错误,重新输入,还有{}次机会".format(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,
    }
    
    while True:
        print('1 淘宝首页\n2 淘宝超市\n3 京东首页\n4 京东超市')
        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('你输入的有非法字符,请重新输入!')


关键字