python 入门 随笔记(一)(装饰器)

xiaoxiao2025-08-05  26

装饰器

先看代码:

def w1(func): print('---装饰前---') def inner(): print('...装饰...') func() return inner @w1 def f1(): print('f1 called') @w1 def f2(): print('f2 called') f1() print("+++++++++++++") f2()

运行结果:

装饰器原理

 

首先,开看我们的装饰器函数w1,该函数接收一个参数func,其实就是接收一个方法名,w1内部又定义一个函数inner,在inner函数中增加装饰,并在装饰后调用传进来的参数func,同时w1的返回值为内部函数inner,其实就是一个闭包函数。

然后,再来看一下,在f1上增加@w1,那这是什么意思呢?当python解释器执行到这句话的时候,会去调用w1函数,同时将被装饰的函数名作为参数传入(此时为f1),根据闭包一文分析,在执行w1函数的时候,此时直接把inner函数返回了,同时把它赋值给f1,此时的f1已经不是未加装饰时的f1了,而是指向了w1.inner函数地址。

接下来,在调用f1()的时候,其实调用的是w1.inner函数,那么此时就会先执行权限验证,然后再调用原来的f1(),该处的f1就是通过装饰传进来的参数f1。

这样下来,就完成了对f1的装饰,实现了装饰。

装饰器知识点

执行时机

了解了装饰器的原理后,那么它的执行时机是什么样呢,接下来就来看一下。  国际惯例,先上代码:

def w1(fun): print('...装饰器开始装饰...') def inner(): print('...验证权限...') fun() return inner @w1 def test(): print('test') test()

运行结果:

由此可以发现,当python解释器执行到@w1时,就开始进行装饰了,相当于执行了如下代码:

test = w1(test)

两个装饰器执行流程和装饰结果

当有两个或两个以上装饰器装饰一个函数时,那么执行流程和装饰结果是什么样的呢?同样,还是以代码来说明问题。

def makeBold(fun): print('----a----') def inner(): print('----1----') return '<b>' + fun() + '</b>' return inner def makeItalic(fun): print('----b----') def inner(): print('----2----') return '<i>' + fun() + '</i>' return inner @makeBold @makeItalic def test(): print('----c----') print('----3----') return 'hello python decorator' ret = test() print(ret)

运行结果:

可以发现,先用第二个装饰器(makeItalic)进行装饰,接着再用第一个装饰器(makeBold)进行装饰,而在调用过程中,先执行第一个装饰器(makeBold),接着再执行第二个装饰器(makeItalic)。

为什么呢,分两步来分析一下。

装饰时机 通过上面装饰时机的介绍,我们可以知道,在执行到@makeBold的时候,需要对下面的函数进行装饰,此时解释器继续往下走,发现并不是一个函数名,而又是一个装饰器,这时候,@makeBold装饰器暂停执行,而接着执行接下来的装饰器@makeItalic,接着把test函数名传入装饰器函数,从而打印’b’,在makeItalic装饰完后,此时的test指向makeItalic的inner函数地址,这时候有返回来执行@makeBold,接着把新test传入makeBold装饰器函数中,因此打印了’a’。 在调用test函数的时候,根据上述分析,此时test指向makeBold.inner函数,因此会先打印‘1‘,接下来,在调用fun()的时候,其实是调用的makeItalic.inner()函数,所以打印‘2‘,在makeItalic.inner中,调用的fun其实才是我们最原声的test函数,所以打印原test函数中的‘c‘,‘3‘,所以在一层层调完之后,打印的结果为<b><i>hello python decorator</i></b> 。

对无参函数进行装饰

上面例子中的f1 f2都是对无参函数的装饰,不再单独举例

对有参函数进行装饰

在使用中,有的函数可能会带有参数,那么这种如何处理呢?  代码优先:

def w_say(fun): """ 如果原函数有参数,那闭包函数必须保持参数个数一致,并且将参数传递给原方法 """ def inner(name): """ 如果被装饰的函数有行参,那么闭包函数必须有参数 :param name: :return: """ print('say inner called') fun(name) return inner @w_say def hello(name): print('hello ' + name) hello('wangcai')

运行结果:

如果多个或者不定长参数呢,该如何处理呢?看看下面的代码你就懂了:

def w_add(func): def inner(*args, **kwargs): print('add inner called') func(*args, **kwargs) return inner @w_add def add(a, b): print('%d + %d = %d' % (a, b, a + b)) @w_add def add2(a, b, c): print('%d + %d + %d = %d' % (a, b, c, a + b + c)) add(2, 4) add2(2, 4, 6)

运行结果:

利用python的可变参数轻松实现装饰带参数的函数。

对带返回值的函数进行装饰

下面对有返回值的函数进行装饰,按照之前的写法,代码是这样的

def w_test(func): def inner(): print('w_test inner called start') func() print('w_test inner called end') return inner @w_test def test(): print('this is test fun') return 'hello' ret = test() print('ret value is %s' % ret)

运行结果:

可以发现,此时,并没有输出test函数的‘hello’,而是None,那是为什么呢,可以发现,在inner函数中对test进行了调用,但是没有接受不了返回值,也没有进行返回,那么默认就是None了,知道了原因,那么来修改一下代码:

def w_test(func): def inner(): print('w_test inner called start') str = func() print('w_test inner called end') return str return inner @w_test def test(): print('this is test fun') return 'hello' ret = test() print('ret value is %s' % ret)

运行结果:

带参数的装饰器

介绍了对带参数的函数和有返回值的函数进行装饰,那么有没有带参数的装饰器呢,如果有的话,又有什么用呢?  答案肯定是有的,接下来通过代码来看一下吧。

def func_args(pre='xiaoqiang'): def w_test_log(func): def inner(): print('...记录日志...visitor is %s' % pre) func() return inner return w_test_log # 带有参数的装饰器能够起到在运行时,有不同的功能 # 先执行func_args('wangcai'),返回w_test_log函数的引用 # @w_test_log # 使用@w_test_log对test_log进行装饰 @func_args('wangcai') def test_log(): print('this is test log') test_log()

运行结果:

简单理解,带参数的装饰器就是在原闭包的基础上又加了一层闭包,通过外层函数func_args的返回值w_test_log就看出来了,具体执行流程在注释里已经说明了。  好处就是可以在运行时,针对不同的参数做不同的应用功能处理。

通用装饰器

介绍了这么多,在实际应用中,如果针对没个类别的函数都要写一个装饰器的话,估计就累死了,那么有没有通用万能装饰器呢,答案肯定是有的,废话不多说,直接上代码。

def w_test(func): def inner(*args, **kwargs): ret = func(*args, **kwargs) return ret return inner @w_test def test(): print('test called') @w_test def test1(): print('test1 called') return 'python' @w_test def test2(a): print('test2 called and value is %d ' % a) test() test1() test2(9)

运行结果:

 

 

类装饰器

装饰器函数其实是一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。  在python中,一般callable对象都是函数,但是也有例外。比如只要某个对象重写了call方法,那么这个对象就是callable的。

当创建一个对象后,直接去执行这个对象,那么是会抛出异常的,因为他不是callable,无法直接执行,但进行修改后,就可以直接执行调用了,如下

class Test(object): def __call__(self, *args, **kwargs): print('call called') t = Test() print(t())

运行结果:

下面,引入正题,看一下如何用类装饰函数。

class Test(object): def __init__(self, func): print('test init') print('func name is %s ' % func.__name__) self.__func = func def __call__(self, *args, **kwargs): print('装饰器中的功能') self.__func() @Test def test(): print('this is test func') test()

运行结果:

和之前的原理一样,当python解释器执行到到@Test时,会把当前test函数作为参数传入Test对象,调用init方法,同时将test函数指向创建的Test对象,那么在接下来执行test()的时候,其实就是直接对创建的对象进行调用,执行其call方法。

好了,到目前为止,基本把python装饰器及相关知识点讲完了

转载请注明原文地址: https://www.6miu.com/read-5034336.html

最新回复(0)