python装饰器原理与用法深入详解

本文实例讲述了python装饰器原理与用法。分享给大家供大家参考,具体如下:

你会Python嘛?
我会!
那你给我讲下Python装饰器吧!
Python装饰器啊?我没用过哎

以上是我一个哥们面试时候发生的真实对白。

----------------------------------------------分割线------------------------------------------------------------------------------

简言之,python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。
一般而言,我们要想拓展原来函数代码,最直接的办法就是侵入代码里面修改,例如:

import time
def func():
  print("hello")
  time.sleep(1)
  print("world")

这是我们最原始的的一个函数,然后我们试图记录下这个函数执行的总时间,那最简单的做法就是:

#原始侵入,篡改原函数
import time
def func():
  startTime = time.time()
  print("hello")
  time.sleep(1)
  print("world")
  endTime = time.time()
  msecs = (endTime - startTime)*1000
  print("time is %d ms" %msecs)

但是如果你的Boss在公司里面和你说:“小祁,这段代码是我们公司的核心代码,你不能直接去改我们的核心代码。”那该怎么办呢,我们仿照装饰器先自己试着写一下:

#避免直接侵入原函数修改,但是生效需要再次执行函数
import time
def deco(func):
  startTime = time.time()
  func()
  endTime = time.time()
  msecs = (endTime - startTime)*1000
  print("time is %d ms" %msecs)
def func():
  print("hello")
  time.sleep(1)
  print("world")
if __name__ == '__main__':
  f = func
  deco(f)#只有把func()或者f()作为参数执行,新加入功能才会生效
  print("f.__name__ is",f.__name__)#f的name就是func

这里我们定义了一个函数deco,它的参数是一个函数,然后给这个函数嵌入了计时功能。然后你可以拍着胸脯对老板说,看吧,不用动你原来的代码,我照样拓展了它的函数功能。

然后你的老板有对你说:“小祁,我们公司核心代码区域有一千万个func()函数,从func01()到func1kw(),按你的方案,想要拓展这一千万个函数功能,就是要执行一千万次deco()函数,这可不行呀,我心疼我的机器。”

好了,你终于受够你老板了,准备辞职了,然后你无意间听到了装饰器这个神器,突然发现能满足你闫博士的要求了。

我们先实现一个最简陋的装饰器,不使用任何语法糖和高级语法,看看装饰器最原始的面貌:

#既不需要侵入,也不需要函数重复执行
import time
def deco(func):
  def wrapper():
    startTime = time.time()
    func()
    endTime = time.time()
    msecs = (endTime - startTime)*1000
    print("time is %d ms" %msecs)
  return wrapper
@deco
def func():
  print("hello")
  time.sleep(1)
  print("world")
if __name__ == '__main__':
  f = func #这里f被赋值为func,执行f()就是执行func()
  f()

这里的deco函数就是最原始的装饰器,它的参数是一个函数,然后返回值也是一个函数。其中作为参数的这个函数func()就在返回函数wrapper()的内部执行。然后在函数func()前面加上@deco,func()函数就相当于被注入了计时功能,现在只要调用func(),它就已经变身为“新的功能更多”的函数了。

所以这里装饰器就像一个注入符号:有了它,拓展了原来函数的功能既不需要侵入函数内更改代码,也不需要重复执行原函数。

#带有参数的装饰器
import time
def deco(func):
  def wrapper(a,b):
    startTime = time.time()
    func(a,b)
    endTime = time.time()
    msecs = (endTime - startTime)*1000
    print("time is %d ms" %msecs)
  return wrapper
@deco
def func(a,b):
  print("hello,here is a func for add :")
  time.sleep(1)
  print("result is %d" %(a+b))
if __name__ == '__main__':
  f = func
  f(3,4)
  #func()

然后你满足了Boss的要求后,Boss又说:“小祁,我让你拓展的函数好多可是有参数的呀,有的参数还是个数不定的那种,你的装饰器搞的定不?”然后你嘿嘿一笑,深藏功与名!

#带有不定参数的装饰器
import time
def deco(func):
  def wrapper(*args, **kwargs):
    startTime = time.time()
    func(*args, **kwargs)
    endTime = time.time()
    msecs = (endTime - startTime)*1000
    print("time is %d ms" %msecs)
  return wrapper
@deco
def func(a,b):
  print("hello,here is a func for add :")
  time.sleep(1)
  print("result is %d" %(a+b))
@deco
def func2(a,b,c):
  print("hello,here is a func for add :")
  time.sleep(1)
  print("result is %d" %(a+b+c))
if __name__ == '__main__':
  f = func
  func2(3,4,5)
  f(3,4)
  #func()

最后,你的老板说:“可以的,小祁,我这里一个函数需要加入很多功能,一个装饰器怕是搞不定,装饰器能支持多个嘛"

最后你就把这段代码丢给了他:

#多个装饰器
import time
def deco01(func):
  def wrapper(*args, **kwargs):
    print("this is deco01")
    startTime = time.time()
    func(*args, **kwargs)
    endTime = time.time()
    msecs = (endTime - startTime)*1000
    print("time is %d ms" %msecs)
    print("deco01 end here")
  return wrapper
def deco02(func):
  def wrapper(*args, **kwargs):
    print("this is deco02")
    func(*args, **kwargs)
    print("deco02 end here")
  return wrapper
@deco01
@deco02
def func(a,b):
  print("hello,here is a func for add :")
  time.sleep(1)
  print("result is %d" %(a+b))
if __name__ == '__main__':
  f = func
  f(3,4)
  #func()
'''
this is deco01
this is deco02
hello,here is a func for add :
result is 7
deco02 end here
time is 1003 ms
deco01 end here
'''

多个装饰器执行的顺序就是从最后一个装饰器开始,执行到第一个装饰器,再执行函数本身。

盗用评论里面一位童鞋的例子:

def dec1(func):
  print("1111")
  def one():
    print("2222")
    func()
    print("3333")
  return one
def dec2(func):
  print("aaaa")
  def two():
    print("bbbb")
    func()
    print("cccc")
  return two
@dec1
@dec2
def test():
  print("test test")
test()

输出:

aaaa
1111
2222
bbbb
test test
cccc
3333

装饰器的外函数和内函数之间的语句是没有装饰到目标函数上的,而是在装载装饰器时的附加操作。

17~20行是装载装饰器的过程,相当于执行了test=dect1(dect2(test)),此时先执行dect2(test),结果是输出aaaa、将func指向函数test、并返回函数two,然后执行dect1(two),结果是输出1111、将func指向函数two、并返回函数one,然后进行赋值。

用函数替代了函数名test。 22行则是实际调用被装载的函数,这时实际上执行的是函数one,运行到func()时执行函数two,再运行到func()时执行未修饰的函数test。

更多关于Python相关内容感兴趣的读者可查看本站专题:《Python面向对象程序设计入门与进阶教程》、《Python数据结构与算法教程》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》、《Python编码操作技巧总结》及《Python入门与进阶经典教程》

希望本文所述对大家Python程序设计有所帮助。

(0)

相关推荐

  • Python中的装饰器用法详解

    本文实例讲述了Python中的装饰器用法.分享给大家供大家参考.具体分析如下: 这里还是先由stackoverflow上面的一个问题引起吧,如果使用如下的代码: 复制代码 代码如下: @makebold @makeitalic def say():    return "Hello" 打印出如下的输出: <b><i>Hello<i></b> 你会怎么做?最后给出的答案是: 复制代码 代码如下: def makebold(fn):    

  • python 装饰器功能以及函数参数使用介绍

    简单的说:装饰器主要作用就是对函数进行一些修饰,它的出现是在引入类方法和静态方法的时候为了定义静态方法出现的.例如为了把foo()函数声明成一个静态函数 复制代码 代码如下: class Myclass(object): def staticfoo(): ............ ............ staticfoo = staticmethod(staticfoo) 可以用装饰器的方法实现: 复制代码 代码如下: class Myclass(object): @staticmethod

  • 深入理解python中的闭包和装饰器

    python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure). 以下说明主要针对 python2.7,其他版本可能存在差异. 也许直接看定义并不太能明白,下面我们先来看一下什么叫做内部函数: def wai_hanshu(canshu_1): def nei_hanshu(canshu_2): # 我在函数内部有定义了一个函数 return canshu_1*canshu_2 return

  • Python中的多重装饰器

    多重装饰器,即多个装饰器修饰同一个对象[实际上并非完全如此,且看下文详解] 1.装饰器无参数: 复制代码 代码如下: >>> def first(func):     print '%s() was post to first()'%func.func_name     def _first(*args,**kw):         print 'Call the function %s() in _first().'%func.func_name         return func

  • Python装饰器用法实例总结

    本文实例讲述了Python装饰器用法.分享给大家供大家参考,具体如下: 一.装饰器是什么 python的装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.简单的说装饰器就是一个用来返回函数的函数. 它经常用于有切面需求的场景,比如:插入日志.性能测试.事务处理.缓存.权限校验等场景.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用. 概括的讲,装饰器的作用就是为已经

  • python重试装饰器示例

    利用python 写一些网络服务的时候,当网络状况不好,或者资源占用过多,任务拥塞的情况下,总会抛出一些异常,当前任务就被终止了,可以很好的利用@装饰器,写一个重试的装饰器,这样比较python!执行结果: 复制代码 代码如下: WARNING:root:timed out, Retrying in 3 seconds...WARNING:root:timed out, Retrying in 6 seconds...WARNING:root:timed out, Retrying in 12

  • 详解Python装饰器由浅入深

    装饰器的功能在很多语言中都有,名字也不尽相同,其实它体现的是一种设计模式,强调的是开放封闭原则,更多的用于后期功能升级而不是编写新的代码.装饰器不光能装饰函数,也能装饰其他的对象,比如类,但通常,我们以装饰函数为例子介绍其用法.要理解在Python中装饰器的原理,需要一步一步来.本文尽量描述得浅显易懂,从最基础的内容讲起. (注:以下使用Python3.5.1环境) 一.Python的函数相关基础 第一,必须强调的是python是从上往下顺序执行的,而且碰到函数的定义代码块是不会立即执行它的,只

  • Python中装饰器高级用法详解

    在Python中,装饰器一般用来修饰函数,实现公共功能,达到代码复用的目的.在函数定义前加上@xxxx,然后函数就注入了某些行为,很神奇!然而,这只是语法糖而已. 场景 假设,有一些工作函数,用来对数据做不同的处理: def work_bar(data): pass def work_foo(data): pass 我们想在函数调用前/后输出日志,怎么办? 傻瓜解法 logging.info('begin call work_bar') work_bar(1) logging.info('cal

  • 介绍Python的@property装饰器的用法

    在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改: s = Student() s.score = 9999 这显然不合逻辑.为了限制score的范围,可以通过一个set_score()方法来设置成绩,再通过一个get_score()来获取成绩,这样,在set_score()方法里,就可以检查参数: class Student(object): def get_score(self): return self._score def set_s

  • python使用装饰器和线程限制函数执行时间的方法

    本文实例讲述了python使用装饰器和线程限制函数执行时间的方法.分享给大家供大家参考.具体分析如下: 很多时候函数内部包含了一些不可预知的事情,比如调用其它软件,从网络抓取信息,可能某个函数会卡在某个地方不动态,这段代码可以用来限制函数的执行时间,只需要在函数的上方添加一个装饰器,timelimited(2)就可以限定函数必须在2秒内执行完成,如果执行完成则返回函数正常的返回值,如果执行超时则会抛出错误信息. # -*- coding: utf-8 -*- from threading imp

  • 详解Python中的装饰器、闭包和functools的教程

    装饰器(Decorators) 装饰器是这样一种设计模式:如果一个类希望添加其他类的一些功能,而不希望通过继承或是直接修改源代码实现,那么可以使用装饰器模式.简单来说Python中的装饰器就是指某些函数或其他可调用对象,以函数或类作为可选输入参数,然后返回函数或类的形式.通过这个在Python2.6版本中被新加入的特性可以用来实现装饰器设计模式. 顺便提一句,在继续阅读之前,如果你对Python中的闭包(Closure)概念不清楚,请查看本文结尾后的附录,如果没有闭包的相关概念,很难恰当的理解P

随机推荐