當前位置:
首頁 > 知識 > Python 中的装饰器

Python 中的装饰器

(点击

上方蓝字

,快速关注我们)




来源:一曲广陵散


segmentfault.com/a/1190000010895903


如有好文章投稿,请点击 → 这里了解详情




一, 引用




[书] 流畅的Python


[书] Effective Python




二, 基本概念




问题1

:装饰器是什么?




解答:

 严格来说,装饰器只是语法糖, 装饰器是可调用的对象,可以像常规的可调用对象那样调用,特殊的地方是装饰器的参数是一个函数




问题2

:装饰器有什么特性?



解答

: 装饰器有2个特性,一是可以把被装饰的函数替换成其他函数, 二是可以在加载模块时候立即执行





def

decorate

(

func

)

:


    

print

(

"running decorate"

,

func

)


    

def

decorate_inner

()

:

        

print

(

"running decorate_inner function"

)


        

return

func

()


    

return

decorate_inner

 


<

a

href

=

"http://www.jobbole.com/members/decorate"

>

@

decorate

</

a

>


def

func_1

()

:


    

print

(

"running func_1"

)


 


if

__name__

==

"__main__"

:


 


    

print

(

func_1

)


 


#返回值


running

decorate

<

function func_1

at

0x7f29f644d268

>


<

function

decorate

.

<

locals

>

.

decorate_inner

at

0x7f29f641cb70

>




问题3

:如何使用被装饰函数中的参数?




解答

: 通过args 和 *kwargs 传递被修饰函数中的参数





def

decorate

(

func

)

:


    

def

decorate_inner

(

*

args

,

**

kwargs

)

:


        

print

(

type

(

args

),

type

(

kwargs

))


        

print

(

"args"

,

args

,

"kwargs"

,

kwargs

)


        

return

func

(

*

args

,

**

kwargs

)


    

return

decorate_inner


 


<

a

href

=

"http://www.jobbole.com/members/decorate"

>

@

decorate

</

a

>


def

func_1

(

*

args

,

**

kwargs

)

:


    

print

(

args

,

kwargs

)


 


if

__name__

==

"__main__"

:


 


    

func_1

(

"1"

,

"2"

,

"3"

,

para_1

=

"1"

,

para_2

=

"2"

,

para_3

=

"3"

)


 


#返回值


<

class

"tuple"

> <

class

"dict"

>


args

(

"1"

,

"2"

,

"3"

)

kwargs

{

"para_2"

:

"2"

,

"para_1"

:

"1"

,

"para_3"

:

"3"

}


(

"1"

,

"2"

,

"3"

)

{

"para_2"

:

"2"

,

"para_1"

:

"1"

,

"para_3"

:

"3"

}




三, 叠放装饰器







问题1

:叠放装饰器执行顺序是什么?




解答:

 如果一个函数被多个装饰器修饰,其实应该是该函数先被最里面的装饰器修饰后(下面例子中函数main()先被inner装饰,变成新的函数),变成另一个函数后,再次被装饰器修饰





def

outer

(

func

)

:


    

print

(

"enter outer"

,

func

)


    

def

wrapper

()

:


        

print

(

"running outer"

)


        

func

()


    

return

wrapper


 


def

inner

(

func

)

:


    

print

(

"enter inner"

,

func

)


    

def

wrapper

()

:


        

print

(

"running inner"

)


        

func

()


    

return

wrapper


 


@

outer


@

inner


def

main

()

:


    

print

(

"running main"

)


 


if

__name__

==

"__main__"

:


 


    

main

()


 


#返回值


 


enter

inner

<

function main

at

0x7fa1c96e8b70

>


enter

outer

<

function

inner

.

<

locals

>

.

wrapper

at

0x7fa1c96e8bf8

>


running outer


running inner


running

main




四, 标准库中的装饰器







问题1

: 标准库中都有哪些装饰器?




解答:

 标准库中有多种装饰器, 例如:装饰方法的函数有property, classmethod, staticmethod; functools模块中的lru_cache, singledispatch,  wraps 等等





from

functools

import

lru_cache


from

functools

import

singledispatch


from

functools

import

wraps




问题2

:为什么要使用@wraps装饰器?它的作用是什么?




解答:

 使用装饰器会产生我们可能不希望出现的副作用, 例如:改变被修饰函数名称,对于调试器或者对象序列化器等需要使用内省机制的那些工具,可能会无法正常运行;其实调用装饰器后,会将同一个作用域中原来函数同名的那个变量(例如下面的func_1),重新赋值为装饰器返回的对象;使用@wraps后,会把与内部函数(被修饰函数,例如下面的func_1)相关的重要元数据全部复制到外围函数(例如下面的decorate_inner)





from

functools

import

wraps


 


def

decorate

(

func

)

:


    

print

(

"running decorate"

,

func

)


    

@

wraps

(

func

)


    

def

decorate_inner

()

:


        

print

(

"running decorate_inner function"

,

decorate_inner

)


        

return

func

()


    

return

decorate_inner


 


<

a

href

=

"http://www.jobbole.com/members/decorate"

>

@

decorate

</

a

>


def

func_1

()

:


    

print

(

"running func_1"

,

func_1

)


 


if

__name__

==

"__main__"

:


 


    

func_1

()


 


#返回值


running

decorate

<

function func_1

at

0x7f145d2c2268

>


running decorate_inner

function

<

function func_1

at

0x7f145b9731e0

>


running

func_1

<

function func_1

at

0x7f145b9731e0

>




五, 装饰器设计模式







问题1

: 什么是装饰器设计模式?




解答:

 动态的给一个对象添加一些额外的职责,就扩展功能而言,装饰器模式比子类化更加灵活,在设计模式中,装饰器和组件都是抽象类,为了给具体的组件添加行为,具体的装饰器实例要包装具体组件的实例,即,装饰器和所装饰的组件接口一致,对使用该组建的客户透明,将客户的请求转发给该组件,并且可能在转发前后执行一些额外的操作,透明性使得可以递归嵌套多个装饰器,从而可以添加任意多个功能




问题2

: Python中的装饰器函数和设计模式中的装饰器模式有什么关系?




解答

:  修饰器模式和Python修饰器之间并不是一对一的等价关系, Python装饰器函数更为强大,不仅仅可以实现装饰器模式。




看完本文有收获?请转

发分享给更多人


关注「P

ython开发者」,提升Python技能


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python开发者 的精彩文章:

Python 开发者面向文档编程的正确姿势
月薪最高 60 k,贝米钱包招大数据架构师、高级Python工程师等技术职位
『应用机器学习的建议』的学习笔记
10 种机器学习算法的要点(附 Python 和 R 代码)

TAG:Python开发者 |