装饰器

装饰器(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

相关推荐

  • Linux下软件包安装(rpm,yum)

    Linux下软件包安装(rpm,yum,源代码) 1)    Rpm包管理安装 2)    Yum包程序管理器 3)    本地Yum仓库建制 ======================================= 1) …

    Linux干货 2016-06-22
  • RAID详解

    测试机centos6.7 x86_64 一、RAID是什么     简单描述:RAID(Redundant Array of indenpensive Disk)独立磁盘冗余阵列:磁盘阵列是把多个磁盘组成一个阵列,当作单一磁盘使用,它将数据以分段或条带(striping)的方式储存在不同的磁盘中,存取数据时,阵列中的相关磁盘一起…

    2016-02-14
  • linux哲学思想

    有关运维,在哲学上的思想。 正确的哲学思想在我看来就是公理,就是像1+1=2一样大家都必须得遵守的公理。 当然,所有的公理都是有前提的,而有关linux的哲学思想当然是在linux环境这个大前提下。才是公理。在我看来,公理就是在学习知识之前所要依靠的准则,linux下的一切都在这些准则下。 比如: 一切接文件  linux下所有都是以文件的形式保存…

    2017-07-22
  • 第十八周博客作业

    1、为LNMP架构添加memcached支持,并完成对缓存效果的测试报告; 操作系统: CentOS 7.210.0.0.51 nginx+php+mysql10.0.0.52 memcached 一. 环境准备: 搭建LNMP编译安装环境 1. 配置163的yum源和阿里云的epel源 [root@localhost ~]# mv /etc/yum.rep…

    2017-07-12
  • 服务器故障的解决方法以及基本脚本的编写

    1,当开机时一直重新启动怎么办? 在开机时就如字符界面后按下a键然后进入单用户模式,通过设置 [root@CentOS6 boot]# vim /etc/inittab 这个文件,将里面的开机启动项改为多用户模式就可。 # id:3:initdefault: “/etc/inittab” 26L, 884C 讲id这项设置完成后重新启动就好。 2,忘记roo…

    Linux干货 2017-05-15