裝飾器

裝飾器(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.www58058.com/88024

(0)
nolannolan
上一篇 2017-10-23
下一篇 2017-10-23

相關推薦

  • http協議

    Web Service概述 web服務時一種應用程序的服務,它所提供的最主要的信息是一種超文本標記語言(HTML)、多媒體資源(如:視頻、圖片、音樂等)。HTML是一種純文字的文本信息,通過所謂的標簽來規范所要顯示的內容格式,在客戶端通過瀏覽器的形式對HTML及多媒體資源進行解析,然后呈現在終端上。主要由http和https協議實現 http協議概述 HTT…

    Linux干貨 2016-10-28
  • 8.3作業

    創建sysadmins組  將user1 user2 user3 加入組中 將user3設置為管理員 用user3登錄,將user2從組中移除 設置sysadmins口令centos 設置user1在創建新文件時所屬組為sysadmins 刪除User1-3 刪除sysadmins [root@localhost testdir]#&nb…

    Linux干貨 2016-08-04
  • 四大開源協議比較:BSD、Apache、GPL、LGPL

          現今存在的開源協議很多,而經過Open Source Initiative組織通過批準的開源協議目前有58種。我們現在常見的開源協議如BSD, GPL, LGPL,MIT等都是OSI批準的協議。如果要開源自己的代碼,最好也是選擇這些被批準的開源協議。   這里我們來看四種最常用的開源協議及它們的適用范圍,供那些準備開…

    系統運維 2016-08-15
  • 【Linux基礎】計算機及操作系統基礎

    【Linux基礎】計算機及操作系統基礎 計算機的組成及其功能 計算機的組成 簡單來說,計算機應由兩部分組成:第一部分:硬件 馮諾依曼結構體系 當前計算機主要是基于馮諾依曼體系結構設計的,在該體系中,計算機硬件由5個部件組成。分別是:運算器、控制器、存儲器、輸入設備和輸出設備。而現今運算器和控制器一般集成在一起,叫中央處理器(Central Processin…

    Linux干貨 2018-03-04
  • 10 文本處理のsed狗帶

    sed 介紹 工作原理 語法 參數選項:-n, -e, -r, -f, -i 地址定界 編輯命令 查找替換 空間操作 練習 參考文檔 sed介紹 sed是一個(stream editor)。         1) :使用sed只能在命令行下輸入編輯命令來編輯文…

    Linux干貨 2016-08-12
  • 馬哥教育網絡21期+第十二周練習博客上

    1、請描述一次完整的http請求處理過程; (1)建立或處理連接:接受請求或拒絕請求 (2)如果接受請求:     并發訪問響應模型:         單進程I/O結構:啟動一個進程處理用戶請求,而且一次只處理一個,多個請求被串行響應; &…

    Linux干貨 2016-10-17
欧美性久久久久