Day 27 函数专题2:装饰器

@浙大疏锦行

今日任务:
  1. 装饰器的思想:进一步复用
  2. 函数的装饰器写法
  3. 注意内部函数的返回值

学习视频:Python小技巧:装饰器(Decorator)

装饰器

为什么需要装饰器?

  • 可读性
  • DRY原则:Don't Repeat Yourself

在编写函数代码的时候,发现有的时候,一个函数内部包含了不只一个功能,这与‘单一职责’相悖。此外,如果采取了分离功能后,后面要想实现同样的功能,重复编写依旧浪费时间。而装饰器正是解决上述问题的存在,引用在评论区看到的:解耦一些通用处理或者不必要功能的,尽可能让一个函数只负责一个任务,避免后续维护时散弹式修改代码。

#质数查找
import time
def is_prime(num):
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2, num):
            if num % i == 0:
                return False
        return True
#函数主体是找质数,但是里面混杂着时间的计算
def prime_nums():
    t1 = time.time()
    for i in range(2, 10000):
        if is_prime(i):
            print(i)
    t2 = time.time()
    print(f"执行时间:{t2 - t1}秒")
prime_nums()

换句话说,装饰器本身就是函数中调用其他函数,实现先拆分函数在合并函数的功能。

基本结构

对于一个装饰器,基本机构包括以下几个部分:

def 装饰器名字(原函数):
    def 新函数(*args, **kwargs): #新函数一般命名为wrapper
        # 添加的新功能
        结果 = 原函数(*args, **kwargs)  # 执行原函数
        # 还可以添加更多新功能
        return 结果
    return 新函数 

其中,原函数如果有参数传入,那么对应的wrapper函数也应有对应的参数(可变数量的参数);如果原函数有return 结果,那么在wrapper函数中也需要return结果

针对上面的质数查找的例子,将时间计算单独提取出来,装饰器编写如下:

import time
def display_time(func): 
    # 定义一个内部函数,在装饰器中wrapper函数是一个常用的函数名,并非强制,约定俗成的
    def wrapper(): #原函数无参数,故不传入
        start_time = time.time()
        func() #调用原函数,func是指装饰器需要修饰的函数,这里func替代的是prime_nums
        end_time = time.time()
        print('Total time:{:.4f}s'.format(end_time-start_time))
    return wrapper # return wrapper是返回函数对象,如果是return wrapper()则是立即执行wrapper函数

增加参数和返回值,再应用装饰器后的代码:

import time
#装饰器
def display_time(func): 
    # 定义一个内部函数,在装饰器中wrapper函数是一个常用的函数名,并非强制,约定俗成的
    def wrapper(*args): #原函数无参数,故不传入
        start_time = time.time()
        result = func(*args) #调用原函数,func是指装饰器需要修饰的函数,这里func替代的是prime_nums
        end_time = time.time()
        print('Total time:{:.4f}s'.format(end_time-start_time))
        return result
    return wrapper # return wrapper是返回函数对象,如果是return wrapper()则是立即执行wrapper函数
#质数查找
def is_prime(num):
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2,num):
            if num % i == 0:
                return False
        return True
@display_time #这行和下一行是一个整体
def count_prime_nums(maxium): 
    count = 0
    for i in range(2,maxium):
        if is_prime(i):
            count += 1
    return count
result = count_prime_nums(10000)
print(result) #1229

装饰器的执行流程

执行流程包括:定义装饰器函数,定义被装饰函数,应用装饰器,替换原函数。

结合上面的例子,具体的流程是:调用count_prime_nums()后,到装饰器display_time()中执行wrapper()函数。然后记录开始时间,调用count_prime_nums()执行质数查找功能(内部会跳转到is_prime()函数),记录结束时间,返回结果(原函数有返回值时必选)。

在上面流程中的‘替换原函数’的意思是,display_time返回的 wrapper 函数,替换了原函数count_prime_nums。装饰后,原函数名指向 wrapper ,而非原始函数。

作业:

编写一个装饰器 logger,在函数执行前后打印日志信息(如函数名、参数、返回值),用昨天的作业题作为被修饰函数:

def logger(func):
    def wrapper(*args,**kwargs):
        print("{}函数开始执行".format(func.__name__))
        print("Arguments:{}".format(args))
        print('Keyword Arguments:{}'.format(kwargs))
        result = func(*args,**kwargs)
        print('{}函数执行完毕,返回值:{}'.format(func.__name__,result))
        return result
    return wrapper
@logger
def describe_shape(shape_name,color='black',**size):
    if not size:
        return f'A {color} {shape_name} with no specific dimensions.'
    based = f'A {color} {shape_name} with dimensions: '
    dimensions = []
    for key,value in size.items():
        dimensions.append(f'{key}={value}')
    based += ','.join(dimensions) + '.' #字符串拼接
    return based
    
#调用
result = describe_shape('turtle','red',length=5,width=4)
print(result) 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值