美文网首页我爱编程
Python进阶话题杂谈(十四)装饰器

Python进阶话题杂谈(十四)装饰器

作者: 樱雨楼 | 来源:发表于2018-08-03 18:54 被阅读0次

装饰器是Python中最难理解的语法之一,但较之其他冷门语法又相对较常用。但有必要指出的是,这里所说的较常用,指的是Python自带的一些装饰器,如@property、@abstractmethod等,自定义装饰器在实际中是较少使用的。

1 高阶函数

函数,作为一段代码的抽象,其本质上也对应于一个表示函数代码起点的内存地址。每当调用一个函数时,程序挂起并跳转到函数所在的地址开始执行,结束后再退回到调用点,继续执行接下来的语句。由此可见,就数据结构而言,函数与数组的原理类似,可以用一个指针来保存函数的起始位置。故在C语言中有“函数指针”类型,且由于函数可以用指针来传递,函数指针就可以作为另一个函数的参数,从而出现了“以函数作为参数的函数”,这样的函数就称为高阶函数。

在Python中,由于对象引用的存在,函数也同样作为一类对象(函数对象),可以通过其引用传递,故在Python中将函数作为参数传递,与其他数据结构的传递是一样的。Python中就有一些常用的高阶函数,如sort、max、min等带有key参数的函数,map、reduce、filter这样的带有函数参数的函数等等。这样的函数都以函数作为参数,并在函数内部调用传入的函数参数。

2 无参装饰器

Python的装饰器被定义为一个高阶函数,这个高阶函数接受一个函数作为其唯一函数,并返回另一个函数作为其唯一返回值,这样的特殊函数就称为装饰器。装饰器函数内部应包含一个完整的函数定义过程,并将这个新的函数作为返回值返回。在此函数内部应包含原函数的调用语句,以及其他额外添加的语句。装饰器定义完成后,就可以使用“@”符号对其他函数进行装饰。

当使用装饰器时,本质上是进行了如下过程:

@staticmethod

def xxx():

   pass

等同于:

def xxx():

   pass

xxx = staticmethod(xxx)

可见,装饰器语法等同于使用装饰器函数处理原函数,并赋值回原函数的过程。

对于装饰器内部定义的新函数,其作为返回值返回后,应保持与原函数一致的函数参数声明,这样才能保证参数正确传递。而装饰器内部定义的函数由于具有通用性,是不能像普通函数一样定义有限多个形参的,故这个问题的解决方案为:使用不定长形参声明,并在函数调用时使用参数解包。

下例定义了一个常见的计时用装饰器:

import time

import numpy as np

def Timer(func):

   def newFunc(*args, **kwargs):

       sTime = time.time()

       returnTuple = func(*args, **kwargs)

       eTime = time.time()

       print('Time: %.6f' % (eTime - sTime))

       return returnTuple

   return newFunc

@Timer

def Test(timesNum):

   for i in range(timesNum):

       np.random.rand(100, 100).dot(np.random.rand(100, 100))

Test(10)

上述代码定义了一个名为Timer的装饰器函数,这个函数接受一个函数作为参数,并在内部定义了一个声明为def newFunc(*args, **kwargs)的通用函数,在这个函数内部,以解包参数*args, **kwargs调用了装饰器传入的函数,并在调用前后保存了调用时间。最终输出消耗时间,并返回函数的返回值。上述这个函数定义,最终作为装饰器函数的返回值返回。在装饰器外部,这个返回的函数将覆盖原函数。所以,在调用被Timer装饰的Test函数时,函数不仅会执行Test函数的内容,还会执行Timer装饰器中所定义的附加内容,即虽然调用的是Test函数,但实际执行的是以Test函数作为参数的newFunc函数。

3 有参装饰器

有参装饰器是“返回装饰器函数的函数”,本人目前并不了解这种语法的具体应用场景,故这里只对有参装饰器做简单的语法上的讨论。

仍然以上文中的计时函数为例,有参装饰器可以使用参数修改装饰器的行为,如定义时间缩放:

import time

import numpy as np

def Timer(mulNum):

   def innerTimer(func):

       def newFunc(*args, **kwargs):

           sTime = time.time()

           returnTuple = func(*args, **kwargs)

           eTime = time.time()

           print('Time: %.6f' % ((eTime - sTime) * mulNum))

           return returnTuple

       return newFunc

   return innerTimer

@Timer(1000)

def Test(timesNum):

   for i in range(timesNum):

       np.random.rand(100, 100).dot(np.random.rand(100, 100))

Test(10)

上述代码中,Timer是一个有参装饰器,其参数用于定义时间缩放值。Timer应返回一个装饰器,故整个装饰器的定义被放在了Timer内部。而innerTimer为装饰器函数,其最终被Timer返回。innerTimer应接受一个函数作为参数,并返回一个新的函数,故在innerTimer内部定义了一个新的函数作为返回值,这个函数的定义中包含了对传入的函数参数func的调用,同时包含了计时语句,并最终利用时间,以及有参装饰器的参数mulNum共同计算输出值。innerTimer装饰器函数返回在其内部定义的函数newFunc,而Timer装饰器函数返回innerTimer装饰器函数,并最终将此装饰器交给被装饰的原函数。

综上,有参装饰器本质上是一个“返回(无参)装饰器函数的任意函数”,而无参装饰器函数是一个“接受函数作为唯一参数,并返回一个新的函数的函数”。有参装饰器只要求返回值是一个无参装饰器即可,而无参装饰器函数将修饰被其装饰的原函数,从而将原函数覆盖为一个装饰后的新函数。从而使得后续代码中所有对原函数的调用,实际调用的都是修饰后的新函数。

相关文章

  • Python进阶话题杂谈(十四)装饰器

    装饰器是Python中最难理解的语法之一,但较之其他冷门语法又相对较常用。但有必要指出的是,这里所说的较常用,指的...

  • Python进阶话题杂谈(十)property装饰器

    property是Python类中的一个非常实用的语法,其主要用于将一些原本需要通过复杂计算而不是直接保存为属性的...

  • Python装饰器(Decorator)完全指南-进阶篇

    Decorator进阶指南 在[python装饰器完全指南基础篇中],我们已经知道了python中的装饰器本质上只...

  • Python装饰器-专题笔记

    学会装饰器,Python更进阶 函数作用域到闭包到装饰器讲解,及闭包和装饰器的运用。 [√] 慕课网Meshare...

  • Python中的装饰器

    本文的内容主要参考了《Python进阶》一书 装饰器(Decorators)是什么? 我理解的装饰器,主要是设计模...

  • Python进阶 装饰器

    一切皆对象 原始的装饰器 使用@的装饰器 蓝本规范 带参数的装饰器 装饰器类

  • python装饰器进阶

    为什么要使用? 使用装饰器使代码变得整洁,并且能解决硬编码问题,使用起来也很方便,但是理解起来相对没那么容易,因此...

  • Python进阶(装饰器)

    note 1:Python内置的@语法就是为了简化装饰器调用。下面两图效果一样。 note 2:python的de...

  • Python进阶 - 装饰器

    函数进阶知识 函数名只是一个指向函数的变量 在python中,一切皆对象。函数名只是一个指向函数的变量,为了验证这...

  • Python进阶-装饰器

    这篇文章要解决的问题: # 装饰器是什么? # 装饰器的种类? # 为什么使用装饰器? # 怎么使用装饰器? # ...

网友评论

    本文标题:Python进阶话题杂谈(十四)装饰器

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