浅析python装饰器

Python函数装饰器浅析

前言:装饰器(Decorators)是python的一个重要的组成部分,本质上是一个python函数,它的返回值也是一个函数对象。对于它的功能,简单来说就是,它可以在不需要做任何代码变动的前提下为所装饰函数添加额外功能。

1.为什么需要装饰器

假设我有这样的一个函数:

def fun(words):
return list(map(len,words)) 
#对列表words中的每个元素计算长度,并返回列表

现在我想要获取这个函数运行的时间,这个时候我们可以修改上述代码,为它添加计时的功能

import time

def fun(b):   
    return list(map(len,b))

t1 = time.time()
fun(['words1','words1words1'])
t2 = time.time()
print(t2-t1)

很简单对不对?但是假如我有很多很多个这样的函数呢?逐一修改每个函数的代码添加计时功能无疑是个庞大的工作量,这个时候我们就需要装饰器了。

前几天看到的一篇文章中对装饰器的功能给了更简介清晰的介绍:

装饰器最大的作用就是对于我们已经写好的程序,我们可以抽离出一些雷同的代码组建多个特定功能的装饰器,这样我们就可以针对不同的需求去使用特定的装饰器,这时因为源码去除了大量泛化的内容而使得源码具有更加清晰的逻辑

2.第一个装饰器的实现

对于装饰器的理解:我们不妨将需要装饰的函数(目的函数)想象成一块糖果(需要实现的主要功能),而装饰器则是包裹在这块糖果外面的包装纸(为其增添的辅助功能)。下面结合一个例子来体会装饰器的包装功能:

#糖块(目的函数):需要实现的主要功能
def say_hello(name = 'fannsy'):
    print("hi " + name )
#装饰器函数:对目的函数进行包装
def decorator(func):
    def wrapper():#wrapper英文含义:包装纸; 封套; 封皮;
        print("befor we meet...")#包装纸
        func()#糖块
        print("after we meet...")#包装纸
    return wrapper

say_hello()
print('--------------')
say_hello = decorator(say_hello)
say_hello()
#运行结果
#hi fannsy
#--------------
#befor we meet...
#hi fannsy
#after we meet...

看明白了吗?python中的装饰器就是为我们的目的函数穿上一层新衣服,“新衣服”就是用来修改目的函数功能和行为的方式。

3.使用@方式生成装饰器

在第二部分,我们讲述了第一个装饰器的生成。下面,我们介绍一种更加简洁的方式生成装饰器。来看下面的代码:

def decorator(func):
    def wrapper():
        print("befor we meet...")
        func()
        print("after we meet...")
    return wrapper

@decorator
def say_hello(name = 'fannsy'):
    print("hi " + name )

say_hello()
#运行结果
#befor we meet...
#hi fannsy
#after we meet...

注意到和第二部分我们生成装饰器代码的不同了吗?

在上一节中我们使用say_hello = decorator(say_hello)语句使能(enable)装饰器。而在本节中,@decorator这个语法相当于执行 say_hello = decorator(say_hello),为say_hello函数装饰并返回

4.functools.wraps函数的使用

到现在位置,你应该对装饰器有了一个基本的理解,但其中还存在一些小问题,如果你运行下面的代码就会发现:

def decorator(func):
    def wrapper():
        print("befor we meet...")
        func()
        print("after we meet...")
    return wrapper

@decorator
def say_hello(name = 'fannsy'):
    print("hi " + name )

print(say_hello.__name__)
#运行结果:wrapper

咦?我们目的函数的__name__属性值怎么变成了wrapper?如果你按照上面的做法操作,那么会wrapper替代我们的目的函数,重写我们函数的名字和注释文档。想要解决这个问题,Python为我们提供了一个简单的函数:functools.wraps

from functools import wraps
def decorator(func):
    @wraps(func)
    def wrapper():
        print("befor we meet...")
        func()
        print("after we meet...")
    return wrapper

@decorator
def say_hello(name = 'fannsy'):
    print("hi " + name )

print(say_hello.__name__)
#运行结果:say_hello

问题成功解决~

5.使用装饰器的蓝本规范

from functools import wraps
def decorator(func):
    @wraps(func)
    def wrapper(*args,**kwargs):
        if not can_run:
            return 'fun is not running'
        return func(*args,**kwargs)
    return wrapper
@decorator
def func():
    print("func is runnning")
can_run = 1
func()
can_run = 0
print(func())
#运行结果
#func is runnning
#fun is not running

部分小伙伴可能对代码中的*args**kwargs存在疑问,wrapper参数为*args, **kwargs。*args表示的参数以列表的形式传入;**kwargs表示的参数以字典的形式传入。

详细请参考python中*args和**kwargs的理解

6.装饰器的运用—日志

日志是装饰器经常运用的一个场景,那么如何运用装饰器将运行的函数写入日志中呢?先看一个小的引例:

from functools import wraps

def logit(func):
    @wraps(func)
    def wrapper(*args,**kwargs):
        print(func.__name__ + " is running")
        return func(*args,**kwargs)
    return wrapper
@logit
def func():
    pass
func()
#运行结果
#func is running

上面的引例实现了如下功能:当运行函数func时,会在控制台输出“func is running”,那么,要实现日志功能,只需将原本在控制台输出的语句写入文件中即可。下面介绍具体实现方法,运用到了带参数的装饰器


def logit(file_name = 'my_logit.log'):
    #************************************#
    def logit_decorator(func):
        @wraps(func)
        #------------------------------#
        def wrapper(*args,**kwargs):
            logit_str = f'{func.__name__} is called'
            print(logit_str)
            with open(file_name,'a') as f:
                f.write(logit_str + '\n')
            return func(*args,**kwargs)
        #------------------------------#
        return wrapper
    #************************************#
    return logit_decorator

@logit()
def func1():
    pass

@logit(logfile='func2.log')
def func2():
    pass

func1()
func2()    
#运行之后,‘my_logit.log’文件中写入了fun1调用信息
#运行之后,	 'func2.log’文件中写入了fun2调用信息

7.类方法的函数装饰器

类方法的函数装饰器与函数的装饰器实现类似,在类方法之前加入@语句即可。

from functools import wraps
def decorator(func):
    @wraps(func)
    def wrapper(*args,**kwargs):
        print("befor we start...")
        func(*args,**kwargs)
        print("done...")
    return wrapper

class class_for_test:
    @decorator
    def add(a,b):
        print(a + b)
a = class_for_test
a.add(1,2)
#运行结果
#befor we start...
#3
#done...

8.装饰器类

之前我们所介绍的都是让一个函数发挥装饰器的作用,在Python中,万物皆对象,类和函数的本质没有什么不一样,因此,类也可以用来构建装饰器。


class Decorator(object):
    
    def __init__(self,func):       
        self.f = func   
    def __call__(self):
        print("befor we start...")
        self.f()
        print("done...")

@Decorator
def func():
    print('func is running')

func()
print('----------------')

def fun():
    print('fun is running')
fun = Decorator(fun)
fun()
#运行结果
#befor we start...
#func is running
#done...
#----------------
#befor we start...
#fun is running
#done...

参考:

https://www.cnblogs.com/lianyingteng/p/7743876.html

http://www.runoob.com/w3cnote/python-func-decorators.html

https://foofish.net/python-decorator.html

https://foofish.net/function-is-first-class-object.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值