python 装饰器

一、开放封闭原则
  1. 对扩展是开放的
      对任何一个程序,我们不可能在程序设计之初时就已经想好了所有要实现的功能,并且将来也不需要做任何更新和修改。所以我们必修允许代码拓展、添加新的功能。
  2. 对修改是封闭的
      比如我们写一个函数,这个函数已经交付给其他人使用了,如果我们对该函数进行修改,那么很可能就会影响正在使用这个函数的用户。
二、装饰器的形成过程

需求: 在不改用函数的调用方式下,给一个已经写好的函数加上打印该函数运行时间的功能。

import time


def get_run_time(my_func):
    def inner():
        start = time.time()
        my_func()
        end = time.time()
        print("time:%.3f" % (end - start))

    return inner


def func():
    print("hello , everybody~")
    print("我是一个已经写好的函数")


func = get_run_time(func)  # 此处func = inner  
func()  # inner()

运行顺序分析
运行顺序
每次调用都都要来一次赋值,如果有一百个函数,那就得进行一百次赋值,这时python开发者们就提出了用一句语法糖来解决这个问题!

import time


def get_run_time(my_func):
    def inner():
        start = time.time()
        my_func()
        end = time.time()
        print("time:%.3f" % (end - start))

    return inner

# 语法糖  @装饰器的函数名
@ get_run_time  # ------> func = get_run_time(func)
def func():  # 被装饰的函数
    print("hello , everybody~")
    print("我是一个已经写好的函数")


func()  # inner()

小结:
  装饰器的本质:一个闭包函数。
  装饰器的功能:在不修改原函数及其调用方式的情况下,对原函数功能进行拓展。
之前两个都是不带参数的装饰器,如果需要加上一个参数,怎么办 ? 很简单,在我们加上装饰器的时候,执行原函数相当于执行闭包函数inner(),那么如果原函数本来就带一个参数,那我们可以在inner( )里也放一个参数即可。

import time


def get_run_time(my_func):
    def inner(name):
        start = time.time()
        my_func(name)
        end = time.time()
        print("time:%.3f" % (end - start))

    return inner


@ get_run_time  # ------> func = get_run_time(func)
def func(name):
    print("hello , everybody~")
    print("我是一个已经写好的函数", name)


func("老哥~~")  # inner("老哥")

when 我需要传入多个参数时,how?? 囊括所有参数的*args,**kwargs…

def get_run_time(my_func):
    def inner(*args, **kwargs):
        start = time.time()
        my_func(*args, **kwargs)
        end = time.time()
        print("time:%.3f" % (end - start))

    return inner


@ get_run_time  # ------> func = get_run_time(func)
def func(name, little_name):
    print("hello , everybody~")
    print("我是一个已经写好的函数", name, little_name)


func("老哥~~", "little_prince")  # inner("老哥~~", "little_prince")

what if 我原函数式有返回值的呢?

import time


def get_run_time(my_func):
    def inner(*args, **kwargs):
        start = time.time()
        to_return = my_func(*args, **kwargs)
        end = time.time()
        print("time:%.3f" % (end - start))
        return to_return

    return inner


@ get_run_time  # ------> func = get_run_time(func)
def func(name, little_name):
    print("hello , everybody~")
    print("我是一个已经写好的函数", name, little_name)
    return little_name


return_thing = func("老哥~~", "little_prince")  # inner("老哥~~", "little_prince")
print(return_thing)
#
# hello , everybody~
# 我是一个已经写好的函数 老哥~~ little_prince
# time:0.000
# little_prince

刚刚那个装饰器已经近乎完美了,但是正常情况下我们查看函数的信息,在这里都会失效。

import time


def get_run_time(my_func):
    def inner(*args, **kwargs):
        start = time.time()
        to_return = my_func(*args, **kwargs)
        end = time.time()
        print("time:%.3f" % (end - start))
        return to_return

    return inner


@ get_run_time  # ------> func = get_run_time(func)
def func(name, little_name):
    """我就是函数文档啦---"""
    print("hello , everybody~")
    print("我是一个已经写好的函数", name, little_name)
    return little_name


print(func.__doc__)  # None    (查看函数文档)
print(func.__name__)  # inner   (查看函数名)

为了不失效,还要在装饰器上面再加上一点东西来完善它,look this

import time
from functools import wraps  # 导入functools 里面的wraps,它能保留原有函数的名称和docstring。


def get_run_time(my_func):
    @wraps(my_func)  # 加在最内层函数的正上方
    def inner(*args, **kwargs):
        start = time.time()
        to_return = my_func(*args, **kwargs)
        end = time.time()
        print("time:%.3f" % (end - start))
        return to_return

    return inner


@ get_run_time  # ------> func = get_run_time(func)
def func(name, little_name):
    """我就是函数文档啦---"""
    print("hello , everybody~")
    print("我是一个已经写好的函数", name, little_name)
    return little_name


print(func.__doc__)  # 我就是函数文档啦    
print(func.__name__)  # func
装饰器的主要功能和装饰器的固定结构
  • 装饰器的主要功能:
    在不改变函数调用方式的基础上在函数的前后添加功能。
  • 装饰器的固定格式
from functools import wraps


def get_run_time(my_func):
    @wraps(my_func)  # 有时候这里不用
    def inner(*args, **kwargs):
        """执行函数之前要做的"""
        to_return = my_func(*args, **kwargs)
        """执行函数之后要做的"""
        return to_return

    return inner
  • 带参数的装饰器(理解成先调用,再装饰原先的无参数的)
    假设你有很多一个函数都是使用了一个装饰器,现在你要将他们全部取消,,how … 后面你发现取消之后不行,还是得用回去 … what_fu…
    这告诉我们给装饰器带上参数,用来选择函数是否需要装饰内容,这是很重要的。
from functools import wraps


def outer(flag):
    def get_run_time(my_func):
        @wraps(my_func)
        def inner(*args, **kwargs):
            if flag:
                print("""执行函数之前要做的""")
            to_return = my_func(*args, **kwargs)
            if flag:
                print("""执行函数之后要做的""")
            return to_return

        return inner
    return get_run_time

#  ------> func = outer(False)(func(a,b)) = get_run_time(func(a,b)) = inner(func(a,b))
@ outer(False)   # 不使用装饰的功能 ==  1]after_outer = outer(False)  2]@after_outer 
def func(name, little_name):
    """我就是函数文档啦---"""
    print("hello , everybody~")
    print("我是一个已经写好的函数", name, little_name)
    return little_name


func("老哥", "little_prince")

# hello , everybody~
# 我是一个已经写好的函数 老哥 little_prince
  • 多个装饰器装饰同一个函数
from functools import wraps


def outer(flag):
    def get_run_time(my_func):
        @wraps(my_func)
        def inner(*args, **kwargs):
            if flag:
                print("""执行函数之前要做的 in outer""")
            to_return = my_func(*args, **kwargs)
            if flag:
                print("""执行函数之后要做的 in outer""")
            return to_return

        return inner
    return get_run_time


def outer_print(flag):
    def print_sb(my_func):
        @wraps(my_func)
        def inner(*args, **kwargs):
            if flag:
                print("sb ")
            to_return = my_func(*args, **kwargs)
            if flag:
                print("sb over")
            return to_return
        return inner
    return print_sb


@outer(True)     # ------> func = outer(True)(func(a,b))  = get_run_time(func(a,b)) = inner(func(a,b))
@outer_print(True)
def func(name, little_name):
    """我就是函数文档啦---"""
    print("hello , everybody~")
    print("我是一个已经写好的函数", name, little_name)
    return little_name


func("老哥", "little_prince")

# 执行函数之前要做的 in outer
# sb 
# hello , everybody~
# 我是一个已经写好的函数 老哥 little_prince
# sb over
# 执行函数之后要做的 in outer

在这里插入图片描述
自行忽略图片中out_print错误,应该为outer_print

作业
1.编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件)要求登录成功一次, 后续的函数都无需再输入用户名和密码。

flag = True
user_info_dict = {}


def get_user_info():
    """"获取用户账户密码信息"""
    with open("user_info", "r+", encoding='utf-8') as f:
        for each_line in f:  # f内容: jacky 001  \n weige 001
            __user_name, __user_passwd = each_line.replace('\n', "").split(" ")
            user_info_dict[__user_name] = __user_passwd
    print(user_info_dict)


def user_log(my_func):
    """装饰器,用于加登录功能"""
    def inner(*args, **kwargs):
        """log in"""
        global flag
        if flag:
            usr_name = input("请输入您的用户名:")
            usr_passwd = input("请输入您的密码:")
            if usr_name in user_info_dict and usr_passwd == user_info_dict[usr_name]:  # 用户名密码正确
                print('登录成功!')
                flag = False
                to_return = my_func(*args, **kwargs)
                return to_return
            else:  # 密码错误
                print('密码错误!')
                return
        else:
            to_return = my_func(*args, **kwargs)
            return to_return
    return inner


@user_log
def f1():
    print("1")


@user_log
def f2():
    print("2")


get_user_info()
f1()
f2()
f2()

2.编写装饰器,为多个函数加上记录调用功能,要求每次调用函数都将被调用的函数写入文件.

from functools import wraps
import datetime


def use_info(my_func):
    """装饰器,添加调用记录"""
    @wraps(my_func)
    def inner(*args, **kwargs):
        write__info(my_func)  # 将函数名写入文件
        to_return = my_func(*args, **kwargs)
        return to_return
    return inner


def write__info(func_name):
    with open("use_info", "a+", encoding="utf-8") as f:
        now_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')  # 将调用的函数名和调用时间写入文件
        f.write(func_name.__name__ + " " + now_time + "\n")


@use_info
def f1():
    print("1")


@use_info
def f2():
    print("2")


f1()
f2()
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值