装饰器

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

相关推荐

  • 浅谈编译kernel+busybox构建拥有远程ssh登录和web功能最小linux系统(二)

    忘了介绍本文的源码的版本了 dropbear-2013.58.tar.bz2    busybox-1.21.1.tar.bz2 linux-3.13.6.tar.xz  nginx-1.4.7 基于上文,我们还差group文件没有写 root@mysql etc]# vi group  …

    Linux干货 2015-09-22
  • zabbix

    一、zabbix简介 Zabbix是一个基于WEB界面的分布式开源监控系统,主要有2部分构成,Zabbix server于Zabbix agent,同时也支持Zabbix proxy。 现在,我们已经将Zabbix大量的应用到了线上环境。在日常的监控中,深切的体验到了Zabbix的高效快捷和优秀的扩展能力。 Zabbix的底层采集程序是用C语言编写的Serv…

    Linux干货 2016-01-05
  • 马哥教育网络班21期+第11周课程练习

    1、详细描述一次加密通讯的过程,结合图示最佳。 首先发送方用单向加密的方法提取数据的特征码,用自己的私钥加密这段特征码,并附加到数据的后面。     发送方用对称加密算法把数据及特征码整个进行加密。     发送方用接收发的公钥加密对称密钥并附加到数据后面。  &nbs…

    Linux干货 2016-09-19
  • generative ai application landscape 1

    Impact and Influence of GenAI on Project Managers Generative AI to Combat Cyber Security Threats For instance, adversaries use generative AI to create sophisticated threats at scal…

    Linux干货 2025-05-10
  • 第四周

    查看链接:http://note.youdao.com/noteshare?id=317ec635a5f28f2681421fd7c9a60f27

    Linux干货 2016-09-19
  • rpm包管理

    一、概述 RPM 是RPM Package Manager(RPM软件包管理器)的缩写。由redhat公司的Redhat package manager改名而来,成了RedHat的工业标准 二、rpm的命名格式: rpm包的命名格式:name-version-relase.arch.rpm version: major.minor.release,同源代码 …

    Linux干货 2015-10-07