前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python3--函数的有用信息,带参数的装饰器,多个装饰器装饰同一个函数

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

作者头像
py3study
发布2018-08-02 16:16:41
3.1K0
发布2018-08-02 16:16:41
举报
文章被收录于专栏:python3python3

开放封闭原则

1 对扩展是开放的

    为什么要对扩展开放呢?

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

2 对修改是封闭的

    为什么要对修改封闭呢?

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

函数的有用信息

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

func1()
print(func1())

执行结果

blob.png
blob.png

例2

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

代码语言:javascript
复制
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

测试

带参数的装饰器

代码语言:javascript
复制
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
blob.png

多个装饰器装饰一个函数

代码语言:javascript
复制
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
blob.png

练习题

代码语言:javascript
复制
# !/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('你输入的有非法字符,请重新输入!')
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-04-03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档