天天看点

对Python装饰器的个人理解方法

  在自己好好总结并对Python装饰器的执行过程进行分解之前,对于装饰器虽然理解它的基本工作方式,但对于存在复杂参数的装饰器(装饰器和函数本身都有参数),总是会感到很模糊,即使这会弄懂了,下一次也很快忘记,其实本质上还是没有多花时间去搞懂其中的细节问题。

        虽然网络上已经有很多这样的文章,但显然都是别人的思想,因此自己总是记不牢,所以花点时间自己好好整理一下。

        最近在对《Python核心编程》做总结,收获了不少,下面分享一下我自己对于Python装饰器的理解,后面还提供了一个较为复杂的Python装饰器的执行过程的分解,可以参考一下。

1.Python装饰器的出现

         在没有装饰器之前,如果要在类中定义一个静态方法,需要使用下面的方法:

1

2

3

<code>class</code> <code>MyClass(</code><code>object</code><code>):</code>

<code>    </code><code>def</code> <code>staticFoo():</code>

<code>        </code><code>staticFoo </code><code>=</code> <code>staticmethod</code><code>(staticFoo)</code>

        即要在该静态方法中加入类似staticmethod()内建函数将该方法转换为静态方法,这显然非常麻烦,而有了装饰器之后,就可以写成下面这样:

4

<code>    </code><code>@</code><code>staticmethod</code>

<code>        </code><code>pass</code>

        这样就简洁很多了。

2.Python装饰器类型与理解

(1)无参数装饰器    

一个装饰器

        下面的情况:

<code>@f</code>

<code>def</code> <code>foo():</code>

<code>    </code><code>pass</code>

        其实就相当于:

<code>foo </code><code>=</code> <code>g(foo)</code>

多个装饰器

<code>@g</code>

        就相当于:

<code>foo </code><code>=</code> <code>g(f(foo))</code>

(2)含参数装饰器

带有参数的一个装饰器

<code>@decomaker</code><code>(deco_args)</code>

<code>foo </code><code>=</code> <code>decomaker(deco_args)(foo)</code>

        用这样的思想去理解就非常好理解了:decomaker()用deco_args做了些事并返回函数对象,而该函数对象正是以foo作为其参数的装饰器。

        下面多个装饰器的例子也是按这样的思想去理解。

带有参数的多个装饰器

<code>@deco1</code><code>(deco_arg)</code>

<code>@deco2</code><code>()</code>

<code>foo </code><code>=</code> <code>deco1(deco_arg)(deco2(foo))</code>

3.Python装饰器执行过程的手动分解

        OK,有了上面的理论基础,理解下面一个较为复杂的装饰器就很容易了:

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

<code>from</code> <code>functools </code><code>import</code> <code>wraps</code>

<code>def</code> <code>log(text):</code>

<code>    </code><code>def</code> <code>decorator(func):</code>

<code>        </code><code>@wraps(func)                    </code><code>#it works like:wraper.__name__ = func.__name__</code>

<code>        </code><code>def</code> <code>wrapper(</code><code>*</code><code>args, </code><code>*</code><code>*</code><code>kwargs):</code>

<code>            </code><code>print</code> <code>'%s %s():'</code> <code>%</code> <code>(text, func.__name__)</code>

<code>            </code><code>return</code> <code>func(</code><code>*</code><code>args, </code><code>*</code><code>*</code><code>kwargs)</code>

<code>        </code><code>return</code> <code>wrapper</code>

<code>    </code><code>return</code> <code>decorator</code>

<code>@log</code><code>(</code><code>'Hello'</code><code>)</code>

<code>def</code> <code>now(area):</code>

<code>    </code><code>print</code> <code>area, </code><code>'2016-01-23'</code>

<code>    </code> 

<code>now(</code><code>'Beijing'</code><code>)</code>

<code>print</code> <code>'The name of function now() is:'</code><code>, now.__name__</code>

        执行如下:

<code>/</code><code>usr</code><code>/</code><code>bin</code><code>/</code><code>python2.</code><code>7</code> <code>/</code><code>home</code><code>/</code><code>xpleaf</code><code>/</code><code>PycharmProjects</code><code>/</code><code>decorator_test</code><code>/</code><code>dec10.py</code>

<code>Hello now():</code>

<code>Beijing </code><code>2016</code><code>-</code><code>01</code><code>-</code><code>23</code>

<code>The name of function now() </code><code>is</code><code>: now</code>

对于该程序的执行过程,可以分析如下:

1.先执行log('Hello')函数,此时返回了一个新的函数,只不过其中的text变量被替换为'Hello',所以用来装饰now函数的新的装饰器如下:

<code>def</code> <code>decorator(func):</code>

<code>    </code><code>@wraps(func)                    </code><code>#it works like:wraper.__name__ = func.__name__</code>

<code>    </code><code>def</code> <code>wrapper(</code><code>*</code><code>args, </code><code>*</code><code>*</code><code>kwargs):</code>

<code>        </code><code>print</code> <code>'%s %s():'</code> <code>%</code> <code>(</code><code>'Hello'</code><code>, func.__name__)</code>

<code>        </code><code>return</code> <code>func(</code><code>*</code><code>args, </code><code>*</code><code>*</code><code>kwargs)</code>

<code>    </code><code>return</code> <code>wrapper</code>

2.所以此时的now函数,就相当于:

<code>now </code><code>=</code> <code>decorator(now)</code>

3.即now就相当于:

<code>def</code> <code>now(</code><code>*</code><code>args, </code><code>*</code><code>*</code><code>kwargs):</code>

<code>    </code><code>print</code> <code>'%s %s():'</code> <code>%</code> <code>(</code><code>'Hello'</code><code>, old_now.__name__)</code>

<code>    </code><code>return</code> <code>old_now(</code><code>*</code><code>args, </code><code>*</code><code>*</code><code>kwargs)</code>

<code># 现在的函数名称变为了now而不是wrapper,是因为使用了wraps装饰器</code>

   所以,输出的结果也就非常好理解了。

        关于wraps,它也是一个装饰器,使用它的作用是,被我们用自定义装饰器修改后的函数,它的函数名称,即func.__name__跟原来是一样的,而它的工作原理正如上面所提及的,即:

<code>wraper.__name__ </code><code>=</code> <code>func.__name__</code>

        也就是说,使用wraps可以不改变原来函数的属性,当然,上面只是简单说明了一下其工作原理,详细的可以参考wraps的源代码。

本文转自 xpleaf 51CTO博客,原文链接:http://blog.51cto.com/xpleaf/1763567,如需转载请自行联系原作者