Python函数式编程-装饰器

本文深入探讨了Python装饰器的概念,解释了如何使用装饰器在不修改原函数代码的情况下为其添加新功能,包括计时器装饰器的实现,以及如何保留被装饰函数的元信息。同时,文章还介绍了多个装饰器的使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.装饰器的定义

在实际的开发过程中,对代码的修改是封闭的,对扩展是开放的。换句话说,提交的代码上线之后是无法修改的!
有时需要在不修改源代码的情况下修改已经存在的函数,给函数增加新的功能 。
以下面例子为例来理解为何要使用装饰器:

def f1():
    print('This is a function...')
    
f1()

假如函数要添加新功能:输出当前的系统时间,直接修改代码可以实现。但是实际中是无法修改原本函数中代码的。

import time
 
def f1():
    # print(time.time())
    print('This is a function...')

f1()

不修改原本函数的代码,通过新的函数也可以实现该功能的添加。

import time
 
def f1():
    print('This is a function...')

def f2():
print('This is a function...')      
    
def print_current_time(func):
    print(time.time())
    func()
    
print_current_time(f1)  
print_current_time(f2) 

问题来了,这样是可以实现功能,也没有改变函数的源代码。但是调用函数的方式也改变了。假如有很多个函数都需要添加新的功能,改动就不方便!所以引入了装饰器这个概念。
装饰器实际上是一个函数。它把一个函数作为输入并且返回另外一个函数。

import time
 
def decorator(func):
    def wrapper():
        print(time.time())
        func()
    return wrapper

@decorator
def f1():		#函数的源代码没有修改
    print('This is a function...')

def f2():
    print('This is a function...')

f1()			#原本的函数调用方式也没有改变
f2()

二.装饰器的使用

wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先实现新增功能,再紧接着调用原始函数。

import time

def decorator(func):
    def warpper(*args,**kwargs):
        print(time.time())
        func(*args,**kwargs)
    return warpper


@decorator
def f1(func_name):
    print('This is a function ' + func_name)

@decorator
def f2(func_name1,func_name2):
    print('This is a function ' + func_name1)
    print('This is a function ' + func_name2)

@decorator
def f3(func_name1,func_name2,**kwargs):
    print('This is a function ' + func_name1)
    print('This is a function ' + func_name2)
    print(kwargs)

f1('test')
f2('test1','test2')
f3('test1','test2',a=1,b=2,c='westos')

练习1:装饰器实现一个函数计时器

"""
1.问题1:被装饰的函数有返回值
2.问题2:如何保留被装饰函数的函数名和帮助信息文档
"""
import random
import string
import time

li = [random.choice(string.ascii_letters) for i in range(100)]

def RunTime(func):
    """这是一个装饰器Runtime"""
    def wrapper(*args,**kwargs):
        """这是一个wrapper函数"""
        start_time = time.time()
        res = func(*args,**kwargs)
        end_time = time.time()
        run_time = end_time - start_time
        print('运行时间为: %.5f' %run_time)
        return res
    return wrapper

@RunTime
def join_add():
    print(','.join(li))

join_add()

@RunTime
def fun_list(n):
    return [i * 2 for i in range(n)]
c=fun_list(10)
print(c)

print(fun_list.__doc__)
print(fun_list.__name__)

装饰器的正确使用:

import functools

def add_log(fun):
    @functools.wraps(fun)
    def wrapper(*args,**kwargs):
        start_time = time.time()
        res = fun(*args,**kwargs)
        end_time = time.time()
        print('[%s] 函数名: %s, 运行时间: %6f,运行返回值结果: %d' %(time.ctime(),fun.__name__,end_time - start_time,res))
        return res
    return wrapper

@add_log
def add(x,y):
    time.sleep(1)
    return x + y

add(1,2)

练习2:

import functools
import inspect

def is_admin(fun):
    @functools.wraps(fun)
    def wrapper(*args,**kwargs):
        #inspect.getcallargs返回一个字典,key值是形参,value值
        #是对应的实参{'name':'root'}
        inspect_res = inspect.getcallargs(fun,*args,*kwargs)
        print('inspect的返回值: %s' %inspect_res)
        if inspect_res.get('name') == 'root':
            res = fun(*args,**kwargs)
            return res
        else:
            print('not root user!')
    return wrapper

@is_admin
def add_user(name):
    print('添加用户信息...')


def del_user(name):
    print('删除用户信息...')

add_user('root')

练习3:

"""
  编写装饰器required_types, 条件如下:
#     1). 当装饰器为@required_types(int,float)确保函数接收到的
每一个参数都是int或者float类型;
#     2). 当装饰器为@required_types(list)确保函数接收到的每一>个参数都是list类型;
#     3). 当装饰器为@required_types(str,int)确保函数接收到的每
一个参数都是str或者int类型;
#     4). 如果参数不满足条件, 打印 TypeError:参数必须为xxxx类
型

"""
import functools


def required_types(*kinds):
    def required_int(fun):
        @functools.wraps(fun)
        def wrapper(*args, **kwargs):
            for i in args:
                if not isinstance(i, kinds):
                    # print('TypeError:参数必须为',kinds)
                    # break
                    raise TypeError('参数必须为%s,%s' % kinds)
            else:
                res = fun(*args, **kwargs)
                return res

        return wrapper

    return required_int


@required_types(float, float)
def add(a, b):
    return a + b


print(add(1.1, 2.0))

三.多个装饰器

一个函数可以有多个装饰器

def decorator_a(fun):
    print('Get in decorator_a')

    def inner_a(*args, **kwargs):
        print('Get in inner_a')
        res = fun(*args, **kwargs)
        return res

    return inner_a


def decorator_b(fun):
    print('Get in decorator_b')

    def inner_b(*args, **kwargs):
        print('Get in inner_b')
        res = fun(*args, **kwargs)
        return res

    return inner_b


@decorator_a
@decorator_b
def f(x):
    print('Get in f')
    return x * 2

f(2)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值