装饰器

装饰器(Decorator)

装饰器推导过程

  • 需求
    • 一个加法函数,想增强它的功能,能够输出被调用过以及调用的参数信息
      def add(x,y):
          return x + y
      
      • 增加信息输出功能
        def add(x,y):
            print('call {},{}+{}'.format(add.__name__, x, y))
            return x + y
        

        59e4a060da105f7aef000006

    • 不足:
      • 打印语句的耦合太高
      • 加法函数属于业务功能,而输出信息的功能,属于非业务功能代码,不该放在业务函数加法中
    • 业务功能分离
      def add(x,y):    return x+ydef logger(fn):    print('begin')  #增强的输出    ret = fn(4,7)    print('end')    #增强的功能    return retlogger(add)

      59e55c9f69daed201d000002

      • 但是fn函数传参是一个问题

        59e55cd769daed201d000003

    • 解决传参
      def add(x,y):    return x+ydef logger(fn,*args, **kwargs):    print('begin')      ret = fn(*args, **kwargs)    print('end')        return retlogger(add,7,9)

       

      • 但又想进一步的简化
    • 柯里化
      def add(x,y):
          return x+y
      
      def logger(fn):
          def wrapper(*args, **kwargs):
              print('begin')  
              ret = fn(*args, **kwargs)
              print('end')    
              return ret
          return wrapper
      
      logger(add)(6,5)
      
      • 调用可以换一种写法
        add = logger(add)
        add(6,5)
        

      59e55d5569daed201d000005

    • 装饰器语法糖
      def logger(fn):    def wrapper(*args, **kwargs):        print('begin')          x = fn(*args, **kwargs)        print('end')            return x    return wrapper```@logger  #等价于 add = logger(add)```def add(x,y):    return x+yadd(6,43)

      59e55d8d69daed201d000006


无参装饰器

  • 装饰器(无参)
    • 它是一个函数
    • 函数作为它的形参
    • 返回值也是一个函数
    • 可以使用@functionname方式,简化调用
  • 装饰器和高阶函数
    • 装饰器是高阶函数,传入和输出都是函数。
    • 但装饰器是对传入函数的功能装饰(功能增强)

应用

获取函数的执行时长,对时长超过阈[yù]值的函数记录一下

import datetime
import time


def logger(fn):
    def wrapper(*args, **kwargs):

        start = datetime.datetime.now()

        print('begin')  
        ret = fn(*args, **kwargs)
        print('end')    

        end = (datetime.datetime.now() - start).total_seconds()
        print('function {} took {}s'.format(fn.__name__,end))

        return ret
    return wrapper

@logger  
def add(x,y):
    print('===call add=========')
    time.sleep(3)
    return x+y

59e570d569daed201d00000b


文档字符串

  • python的文档
    • python是文档字符串Documentation Strings
    • 在函数语句块的第一行,且习惯是多行的文本,所以多使用三引号
    • 惯例是首字母大写,第一行写概述,空一行,第三行写详细描述
    • 可以使用特殊属性doc访问这个文档
def add(x,y):
    """
    This is a function of addition

    int x
    int y
    return int
    """
    a = x+y
    return x + y

print("name={}\ndoc={}".format(add.__name__, add.__doc__))
print(help(add))

59e559ce69daed201d000000


文档字符串应用

凡是被装饰的函数都需要复制这些属性,这个函数很通用

  • 副作用
    def logger(fn):
        def wrap(*args,**kwargs):
            '''
            This is a wrapper
            '''
            print('begin')
            ret = fn(*args,**kwargs)
            print('end')
            return ret
        return wrap
    
    @logger
    def add(x,y):
        '''
        This is a function of addition
    
        int x
        int y
        return int
        '''
        return x + y
    
    print('name = {}, doc = {}'.format(add.__name__,add.__doc__))
    

    59e5610d69daed201d000007

    • 原函数对象的属性都被替换了,而使用装饰器,我们的需求是查看被封装函数的属性
    • 也就是想看到add的属性,结果显示的是wrap的
    • 如何解决?

  • 提供一个函数,被封装函数属性 copy 成 包装函数属性
    def copy_properties(src,dst):
        dst.__name__ = src.__name__
        dst.__doc__ = src.__doc__
    
    def logger(fn):
        def wrap(*args,**kwargs):
            '''
            This is a wrapper
            '''
            print('begin')
            ret = fn(*args,**kwargs)
            print('end')
            return ret
        copy_properties(fn,wrap)
        return wrap
    
    @logger
    def add(x,y):
        '''
        This is a function of addition
    
        int x
        int y
        return int
        '''
        return x + y
    
    print('name = {}, doc = {}'.format(add.__name__,add.__doc__))
    

    59e5661569daed201d000008


  • copy_properties可以改造成装饰器
    def copy_properties(src):    def _copy(dst):        dst.__name__ = src.__name__        dst.__doc__ = src.__doc__        return dst    return _copydef logger(fn):    @copy_properties(fn)    def wrap(*args,**kwargs):        '''        This is a wrapper        '''        print('begin')        ret = fn(*args,**kwargs)        print('end')        return ret    return wrap@loggerdef add(x,y):    '''    This is a function of addition    int x    int y    return int    '''    return x + yprint('name = {}, doc = {}'.format(add.__name__,add.__doc__))#第9行等价于 wrap = copy_properties(fn)(wrap)

    59e5698069daed201d000009


带参装饰器

  • 它是一个函数
  • 函数作为它的形参
  • 返回值是一个不带参的装饰器函数
  • 使用@functionname(参数列表)方式,简化调用
  • 可以看做在装饰器外层又加了一层函数

应用

获取函数的执行时长,对时长超过阈[yù]值的函数记录一下

import datetime
import time

def logger(t1,t2):
    def _logger(fn):
        def wrapper(*args, **kwargs):

            start = datetime.datetime.now()

            print('begin')  
            ret = fn(*args, **kwargs)
            print('end')  

            delta = (datetime.datetime.now() - start).total_seconds()
            if delta > t1 and delta < t2:
                print('function {} took {}s'.format(fn.__name__,delta)

            return ret
        return wrapper
    return _logger

@logger(3,5) 
def add(x,y):
    print('===call add=========')
    time.sleep(6)
    return x+y

59e575c269daed201d00000f


  • 将记录的功能提取出来,这样就可以通过外部提供的函数来灵活的控制输出
    import datetime
    import time
    
    def logger(duration, func = lambda name,t1: print('{} took {}s'.format(name,t1))):
        def _logger(fn):
            def wrapper(*args, **kwargs):
    
                start = datetime.datetime.now()
    
                print('begin')  
                ret = fn(*args, **kwargs)
                print('end')  
    
                delta = (datetime.datetime.now() - start).total_seconds()
                if delta > duration:
                    func(fn.__name__, delta)
    
                return ret
            return wrapper
        return _logger
    
    @logger(3) 
    def add(x,y):
        print('===call add=========')
        time.sleep(6)
        return x+y

59e573ed69daed201d00000e

本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/88024

(0)
nolannolan
上一篇 2017-10-23 08:54
下一篇 2017-10-23 10:35

相关推荐

  • M22 xfs文件系统命令总结

    Centos6.0及之前的默认文件系统是ext系列的,而在最新的Centos7上却使用了xfs作为默认的文件系统。在Centos7中xfs文件系统包括如下命令: xfs_admin:修改xfs文件系统的参数,需要先卸载分区 用法:xfs_admin [ -eflpu ] [ -c 0|1 ] [ -L label ] [ -U uuid ] device x…

    Linux干货 2017-03-16
  • MySQL优化大全

     1. 优化SQL   1)通过show status了解各种sql的执行频率         show status like 'Com_%'        了解 Com_select,Com_in…

    Linux干货 2015-04-13
  • CentOS 7 yum安装LAMP,LNMP并搭建WordPress个人博客网站

    本次实验要进行的是在CentOS7.2,内核版本3.10.0-327.el7.x86_64的环境下搭建LAMP和LNMP,并在此之上做一个WordPress博客网站。 [root@Shining ~]# uname -a Linux Shining.ACG 3.10.0-327.el7.x86_64&nbsp…

    Linux干货 2016-11-24
  • N25-第四周作业

    1、复制/etc/skel目录为/home/tuser1,要求/home/tuser1及其内部文件的属组和其它用户均没有任何访问权限。 [root@localhost ~]# cp -r /etc/skel /home/tuser1 [root@localhost ~]# ls …

    Linux干货 2017-01-03
  • linux文件权限

                                                      &nbsp…

    Linux干货 2016-08-04
  • 马哥教育网络班22期第二周课程练习1-未闻花名

    1、Linux管理文件和目录的命令 命令 功能 命令 功能 pwd 显示当前目录 ls 查看目录下的内容 cd 改变所在目录 cat 显示文件的内容 grep 在文件中查找某字符 cp 复制文件 touch 创建文件 mv 移动文件 rm 删除文件 rmdir 删除目录 1.1 pwd命令 该命令的英文解释为print working directory(打…

    Linux干货 2016-08-29