装饰器

装饰器

一、什么是装饰器

         ”装饰“代指为被装饰对象添加新的功能,“器”代指器具/工具,装饰器与被装饰的对象均可以是任意可调用对象。概括地讲,装饰器的作用就是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能。装饰器经常用于有切面需求的场景,比如:插入日声、性能测试、事务处理、缓存、权限校验等应用场景,装饰器是解决这类问题的绝佳设计,有了装饰器,就可以抽离出大量与函数功能本身无关的协同代码并继续重用。

         可调用对象有函数,方法或者类,此处我们单以本章主题函数为例,来介绍函数装饰器,并且被装饰的对象也是函数。

二、为何要用装饰器

         软件的设计应该遵循开放封闭原则,即对扩展是开放的,而对修改是封闭的。对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。对修改封闭,意味着对象一旦设计完成,就可以独立完成其工作,而不要对其进行修改。

三、装饰器的实现

函数饰器分为:无参装饰器和有参装饰两种,二者的实现原理一样,都是“函数嵌套+闭包+函数对象”的组合使用的产物。

1.无参装饰器的实现

         如果想为下述函数添加统计其执行时间的功能

         impor time

         def index():

                   time.sleep(3)

                   print(“welcome to the indexpage”)

                   return 200

         index()

遵循不修改被装饰对象对源代码的原则,我们想到的解决方法可能是这样

         start_time = time.time()

         index(x)

         stop_time = time.time()

         print(“run time is %s”%(stop_time-start_time))


         wrapper(index)

         wrapper(其他函数)


这便违反了不能修改被装饰对象调用方式的原则,于是我们换一种为函数体传值的方式,即将值包给函数

def timer(func):

         def wrapper():

                   start_time = time.time()

                   res = func()

                   stop_time=time.time()

                   print(“run time is %s”%(stop_time-start_time))

                   return res

         return wrapper

这倦我们便可以在不修改被装饰函数源代码和调用方式的前提下为其加上统计时间的功能,只不过需要事先执行一次time将被装饰的函数传入,返回一个闭包函数wrapper重新赋值给变量名/函数名index

         index = timer(index)

         index()

修正装饰器time如下

def timer(fcun):

         def wrpper(*args,**kwargs):

                   start_time = time.time()

                   res = fun(*args,**kwargs)

                   stop_time = time.time()

                   print(“run time is %s”%(stop_time-start_time))

                   return res

         return wrapper


         为了简洁而优雅地使用装饰器python提供了专门的装饰器语法来取代index=timer(index)的形式,需要在被装饰对象的正上方单独一行添加@timer,当解释器到@timer时就会调用timer函数,且把它正下方的函数名当做实参传入,然后将返回的结果重新赋值给原函数名

         @timer

         def index():

                   time.sleep(3)

                   print(“welcome to the indexpage”)

                   return 200

         @timer

                   time.sleep(5)

                   print(“welcome to the homepage”,name)

         如果我们有多个装饰器,可以叠加多个

         @deco3

         @deco2

         @deco1

         def index():

                   pass

         2.有参装饰器的实现

         了解无参装饰器的实现原理后,我们可以再实现一个用来为被装饰对象添加认证功能的装饰器,实现的基本形式如下

         def deco(func):

                   def wrapper(*args,**kwargs):

                            if driver =  “file”:

                                     编写基于文件的认证,认证通过则执行res=fun(*args,**kwargs),并返回                                 res

                            elif driver == “mysql”:

                   retun wrapper


函数wrapper需要一个driver参数,而函数deco与wrapper的参数都有其特定的功能,不能用来接受其他类别的参数,可以在deco的外部再包一层函数auth,用来专门接受处的参数,这样便保证了在auth函数内无论多少层都可以引用到

此时我们就实现了一个有参装饰器

先调用auth_type(driver=”file’),得到@deco,deco是个闭包函数

包含了对外部作用域名字driver的引用,@deco的语法意义与无参装饰器一样

@auth(driver=”file”)

Def index():

         Pass

@auth(driver=”mysql”)

Def home():

         Pass

可以使用help(函数名)来查看函数的文档注释,本质就是查看函数的doc属性,但对于被装饰之后的函数,查看文档注释

@timer

def home(name):

         time.sleep(5)

         print(“welcome to the home page”,name)

print(help(home))

在被装饰之后home = wrapper,查看home.name也可以发现home的函数名确实是wrapper,想要保留原函数的文档和函数名属性,需要修正装饰器

def timer(func):

         def wrapper(*argw,**kwargs):

                   start_time=time.time()

                   res=func(*args,**kwargs)

                   stop_time=time.time()

                   print(“run time is%s”,%(stop_time-start_time))

                   return res

         wrapper._doc_=func._doc_

         wrapper._name_=func._name_

         return wrapper

按照上述方式来实现保留原函数属性过于麻烦,functools模块下提供一个装饰器wraps专门用来帮我们实现这件事

         from functools import wraps

         def timer(func):

                   @wraps(func)

                   def wrapper(*args,**kwargs):

                            start_time=time.time()

                            res = func(*args,**kwargs)

                            stop_time=time.time()

                            print(“run time is%s:%(stop_time-start_time))

                            return res

                   return wrapper

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 在学习Python的过程中,我相信有很多人和我一样,对Python的装饰器一直觉得很困惑,我也是困惑了好久,并通过...
    愚灬墨阅读 3,222评论 1 1
  • 装饰器函数 楔子 作为一个会写函数的python开发,我们从今天开始要去公司上班了。写了一个函数,就交给其他开发用...
    go以恒阅读 1,891评论 0 0
  • 装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返...
    胡一巴阅读 3,114评论 0 0
  • 一、装饰器的基本使用 在不改变函数源代码的前提下,给函数添加新的功能,这时就需要用到“装饰器”。 0.开放封闭原则...
    NJingZYuan阅读 3,499评论 0 0
  • 本文的目录如下:装饰器语法糖入门用法:日志打印器入门用法:时间计时器进阶用法:带参数的函数装饰器高阶用法:不带参数...
    A遇上方知友阅读 4,196评论 0 0