装饰器

装饰器(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
下一篇 2017-10-23

相关推荐

  • CentOS7口令破解

    一.rd.break方式修改  1.启动时任意键暂停启动      2.按e 键进入编辑模式 将光标移动linux16 开始的行,添加内核参数rd.break 按ctrl-x 启动 3.重新挂载可读可写根目录 :mount –o remount,rw /sysroot 切回根目录: chroot /sysroot 修改…

    Linux干货 2016-11-29
  • 软件编译安装小结

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://nolinux.blog.51cto.com/4824967/1439073     本文主要针对 configure 和 cmake 做一个介绍以及我们重新配置编译参数文件时,都需要做哪些清…

    Linux干货 2016-08-15
  • 0808文本处理作业

                           1 、删除/etc/grub2.conf 文件中所有以空白开头的行行首的空白字符。 答:sed  ‘@^[[:space]]\+@@’  /etc/grub2/cf…

    Linux干货 2016-08-11
  • N25第八周博客作业

    1、写一个脚本,使用ping命令探测172.16.250.1-172.16.250.254之间的所有主机的在线状态;     在线的主机使用绿色显示;     不在线的主使用红色显示; #!/bin/bash trap ‘mytrap’ INT mytra…

    Linux干货 2017-02-26
  • N25-第九周博客作业

    1、写一个脚本,判断当前系统上所有用户的shell是否为可登录shell(即用户的shell不是/sbin/nologin);分别这两类用户的个数;通过字符串比较来实现; #!/bin/bash # 统计登录用户和非登陆用户的个数 # author: han declare -i loginnum=0 declare -i nologinnum=0 whil…

    Linux干货 2017-02-24
  • 第二周作业

    一、文件管理命令以及演示方法  1.1. mkdir命令     作用:创建文件夹     基本用法:mkdir [OPTION]… DIRECTORY…     参数:   &nbsp…

    Linux干货 2016-08-22