美文网首页
python学习笔记之--函数装饰器

python学习笔记之--函数装饰器

作者: itsenlin | 来源:发表于2022-01-16 17:10 被阅读0次

概念

函数装饰器也是一种函数,其参数是被装饰的函数。只在函数声明/定义的时候使用,而在函数调用时不显示体现
装饰器放在被装饰的函数之上,以@开头,后面跟装饰器名字,以及可选参数,如下

@decoratorName[(args)]
def funcName(func_args):
    func_suit

在调用函数时,如下

funcName(...)

会自动转换成,如下形式执行

decoratorName(funcName)

使用场景

python的装饰器类似于Spring框架中使用的AOP(面向切面编程)。
主要使用场景为:

  1. 受权框架
  2. 日志框架
  3. 类中的staticmethod/classmethod方法等

自定义装饰器

回顾

在学习python的函数时知道,python语言的函数类似go语言为一等公民,即可以当成一个普通的类型来使用。

  • 函数可以赋值给另外一个变量
    >>> def hi(name='alex'):
    ...     print('hi %s' % name)
    ... 
    >>> h = hi
    >>> h()
    hi alex
    >>>
    
  • 函数可以做为另一个函数的参数
    >>> def log(func, arg):
    ...     func(arg)
    ... 
    >>> log(hi, 'lucy')
    hi lucy
    >>>
    
  • 函数可以定义在另一个函数体内,即嵌套定义
    >>> def foo():
    ...     def bar():
    ...         print('call foo.bar()')
    ...     bar()
    ... 
    >>> foo()
    call foo.bar()
    >>>
    
  • 函数可以做为另一个函数的返回值
    >>> def foo():
    ...     def bar():
    ...         print('call foo.bar()')
    ...     return bar
    ...
    >>> b = foo()
    >>> b()
    call foo.bar()
    >>> 
    

装饰器函数即是通过python函数的这些特性来实现的,具体实现参见下面章节

不带参数的装饰器

既然装饰器是为了装饰函数、给原有函数增加特殊功能,说明最主要的还是要调用到被装饰函数,这样我们可以把被装饰函数做为参数传给装饰器,在装饰器中实现一个嵌套函数,此内嵌函数实现对被装饰函数的封装及调用,如下

>>> def decorator_test(func):
...     def wrapper_func():
...         print("--------before-------")
...         func()
...         print("--------after-------")
...     return wrapper_func
... 
>>> @decorator_test
... def foo():
...     print('call foo()')
... 
>>> foo()
--------before-------
call foo()
--------after-------
>>>

但是这样会有一个问题,foo函数虽然调用时跟不使用装饰器一致,但是实际上基__name__变量已经被修改为装饰器里面的内嵌函数了,如下

>>> foo.__name__
'wrapper_func'
>>>

有没有办法即使用装饰器,又不要改变原有函数的一些属性呢(有可能这些属性会被使用)?

python提供了一个functools模块,里面的wraps函数(也是一个装饰器)可以实现复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。使用方法如下

>>> from functools import wraps
>>> 
>>> def decorator_test(func):
...     @wraps(func)
...     def wrapper_func():
...         print("--------before-------")
...         func()
...         print("--------after-------")
...     return wrapper_func
... 
>>> @decorator_test
... def foo():
...     print('call foo()')
... 
>>> foo()
--------before-------
call foo()
--------after-------
>>> foo.__name__
'foo'
>>>

带参数的装饰器

如果装饰器函数需要使用参数,则需要在不带参数的装饰器场景下再增加一层函数嵌套,最外层返回一个装饰器,具体实现参见下面这个例子

>>> from functools import wraps
>>> 
>>> def log(txt='call'):
...     def decorator_test(func):
...         @wraps(func)
...         def wrapper_func():
...             print("--------before %s-------" % txt)
...             func()
...             print("--------after %s-------" % txt)
...         return wrapper_func
...     return decorator_test
... 
>>> @log('calling func')
... def foo():
...     print('call foo()')
... 
>>> foo()
--------before calling func-------
call foo()
--------after calling func-------
>>>

可以对比一下前面不带参数的装饰器的例子

被装饰的函数带参数情况

前面的例子中被装饰的函数foo()都是无参情况,实现比较简单。
但是一般情况下装饰器都是一些比较通用的,装饰器一般不知道被装饰的函数是否有参数,以及有几个参数,这种情况该如何处理呢?

这个就使用到python函数不定长参数的实现方式了,具体实现如下

>>> from functools import wraps
>>> 
>>> def log(txt='call'):
...     def decorator_test(func):
...         @wraps(func)
...         def wrapper_func(*args, **kwargs):
...             print("--------before %s-------" % txt)
...             func(*args, **kwargs)
...             print("--------after %s-------" % txt)
...         return wrapper_func
...     return decorator_test
... 
>>> @log('calling func')
... def foo():
...     print('call foo()')
... 
>>> @log()
... def bar(name='alex'):
...     print('hi %s' % name)
... 
>>> foo()
--------before calling func-------
call foo()
--------after calling func-------
>>> bar()
--------before call-------
hi alex
--------after call-------
>>> bar('lucy')
--------before call-------
hi lucy
--------after call-------
>>>

装饰器类

python中有一个特殊内部函数__call__可以实现把类对象当成函数形式的使用

例如:
a为一个类对象,b为一个类中定义的方法,访问需要通过a.b()形式;
如果类中实现了__call__,对象a可以被当成一个函数使用

如下:

>>> class Demo:
...     def __init__(self, a, b):
...         self.a = a
...         self.b = b
...     def my_print(self,):
...         print("a = ", self.a, "b = ", self.b)
...     def __call__(self, *args, **kwargs):
...         self.a = args[0]
...         self.b = args[1]
...         print("call: a = ", self.a, "b = ", self.b)
... 
>>> a = Demo(1, 2)
>>> a.my_print()
a =  1 b =  2
>>> a(3, 4)
call: a =  3 b =  4
>>> 

根据类的这个特性,我们可以在__call__中实现装饰器,这样类名就可以做为装饰器使用了

>>> from functools import wraps
>>> 
>>> class log:
...     def __init__(self, txt='call'):
...         self.txt = txt
...     def __call__(self, func):
...         @wraps(func)
...         def wrapper_func(*args, **kwargs):
...             print("--------before %s-------" % self.txt)
...             func(*args, **kwargs)
...             print("--------after %s-------" % self.txt)
...         return wrapper_func
... 
>>> @log('calling func')
... def foo():
...     print('call foo()')
... 
>>> @log()
... def bar(name='alex'):
...     print('hi %s' % name)
... 
>>> foo()
--------before calling func-------
call foo()
--------after calling func-------
>>> bar()
--------before call-------
hi alex
--------after call-------
>>> bar('lucy')
--------before call-------
hi lucy
--------after call-------
>>> 

这样我们就可以使用类的继承、覆盖等特性实现更复杂的场景

相关文章

  • Python ☞ day 5

    Python学习笔记之 装饰器& 偏函数 & 异常处理 & 断言 & 文件读写 &编码与解码 装饰器 概念:是一个...

  • Python ☞ day 4

    Python学习笔记之 (set)集合 & 迭代器 & 函数 & 匿名函数 & 高阶函数 set set:类似di...

  • python学习笔记之--函数装饰器

    概念 函数装饰器也是一种函数,其参数是被装饰的函数。只在函数声明/定义的时候使用,而在函数调用时不显示体现装饰器放...

  • python装饰器

    装饰器简述 要理解装饰器需要知道Python高阶函数和python闭包,Python高阶函数可以接受函数作为参数,...

  • python 函数式编程之装饰器

    python学习笔记,特做记录,分享给大家,希望对大家有所帮助。 装饰器 由于函数也是一个对象,而且函数对象可以被...

  • python之装饰器

    Python之装饰器 装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功...

  • Python装饰器

    本篇将介绍Python的装饰器用法,更都内容请参考: Python学习指南 装饰器 由于函数也是一个对象,而且函数...

  • python 装饰器 补充

    重新理解python 装饰器 python 装饰器是一个函数,被装饰器所装饰的代码块最终也是一个函数这个对于一般的...

  • Python闭包和装饰器

    本节课纲: 魔法方法之_call_ 闭包 装饰器 装饰器实例 一、魔法方法之_call_ 在Python中,函数其...

  • Flask 视图和路由的进阶技能

    视图装饰器 Python 装饰器是用于转换其它函数的函数。当一个装饰的函数被调用的时候,装饰器也会被调用。接着装饰...

网友评论

      本文标题:python学习笔记之--函数装饰器

      本文链接:https://www.haomeiwen.com/subject/gcmfhrtx.html