上下文管理練習(為加法函數計時)

上下文管理(為加法函數計時)

  • 為加法函數計時
    • 使用裝飾器顯示該函數的執行時長
    • 使用上下文管理顯示該函數的執行時長

裝飾器實現

import time
import datetime
from functools import wraps

def logger(fn):
    @wraps(fn)    # wraps(fn)(wrapper)
    def wrapper(*args, **kw):
        start = datetime.datetime.now()
        ret = fn(*args, **kw)
        delta = (datetime.datetime.now() - start).total_seconds()
        print(delta)
        return ret
    return wrapper

@logger
def add(x, y):
    time.sleep(2)
    return x + y

上下文實現

  • 最簡單實現
    5a0cea2230e4d913bf000000

  • 增加__call__用法
    import time
    import datetime
    
    class TimeIt:
        def __init__(self, fn):
            self.fn = fn
    
        def __enter__(self):
            self.start = datetime.datetime.now()
            return self
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.delta = (datetime.datetime.now() - self.start).total_seconds()
            print(self.delta)
    
        def __call__(self, *args, **kwargs):
            ret = self.fn(*args, **kwargs)
            return ret
    
    def add(x, y):
        """ This is add"""
        time.sleep(2)
        return x + y
    
    with TimeIt(add) as foo:
        print(foo(3,4))
    

    5a0ceac630e4d913bf000001

    • 需要增加初始化方法,為下面__call__使用
    • __enter__方法返回self是為了,有了__call__方法以后,可以這樣使用with TimeIt(add) as foo: foo(3,4)
      • 因為有了__call__后,實例變成可調用,而foo就是實例化后的實例
      • TimeIt(add)是將add函數名作為形參傳進去

  • 改成裝飾器
    import time
    import datetime
    
    class TimeIt:
        def __init__(self, fn):
            self.fn = fn
    
        def __enter__(self):
            self.start = datetime.datetime.now()
            return self
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.delta = (datetime.datetime.now() - self.start).total_seconds()
            print(self.delta)
    
        def __call__(self, *args, **kwargs):
            print('call')
            ret = self.fn(*args, **kwargs)
            return ret
    
    @TimeIt          # add = TimeIt(add)
    def add(x, y):
        """ This is add"""
        time.sleep(2)
        return x + y
    
    # 上下文用法
    # with TimeIt(add) as foo:
    #     print(foo(3,4))
    
    print(add(5,6))
    print(add.__doc__)
    print(add.__dict__)
    print(add.__name__)
    

    5a0cf0b330e4d913bf000005
    5a0cef5530e4d913bf000004

    • 裝飾器用法很簡單,直接在add函數上加個@TimeIt
      • 這是由于@TimeIt等價于add = TimeIt(add),把add函數名作為實參傳入到TimeIt類中
      • 就相當于為add函數加了一個類封裝的功能或屬性
    • 而這個時候,我們發現,用裝飾器實現后,直接走的是__call__方法中的語句塊,而上下文沒有執行(因為沒有用with..as語句)
    • 也就是說,要么用with..as語句,要么用裝飾器方法,這是兩個方法
      • (用with..as語句執行裝飾器方法,這樣比較繁瑣,重復計算,因題而異)
    • 但是,如何解決文檔字符串的問題,怎么把add函數的配置信息也弄過來(看__doc__和__dict__就可以知道)

  • 解決文檔字符串問題
    • 方法1
    • 把函數對象的文檔字符串賦給類
      class TimeIt:
          def __init__(self, fn):
              self.fn = fn
              self.__doc__ = self.fn.__doc__
              self.__name__ = self.fn.__name__
              self.__dict__ = self.fn.__dict__
      

    • 方法2
    • 使用functools.wraps函數

最終完整版

import time
import datetime
from functools import wraps

class TimeIt:
    """This is Class"""
    def __init__(self, fn):
        self.fn = fn

        # 把函數對象的文檔字符串賦給類
        # self.__doc__ = self.fn.__doc__
        # self.__name__ = self.fn.__name__

        # @wraps = wraps(fn)(wrapper)
        wraps(fn)(self)   # wraps用法

#     def __enter__(self):
#         self.start = datetime.datetime.now()
#         return self

#     def __exit__(self, exc_type, exc_val, exc_tb):
#         self.delta = (datetime.datetime.now() - self.start).total_seconds()
#         print(self.delta)

    def __call__(self, *args, **kwargs):
        print('call')
        self.start = datetime.datetime.now()
        ret = self.fn(*args, **kwargs)
        delta = (datetime.datetime.now() - self.start).total_seconds()
        print(delta)
        return ret

@TimeIt          # add = TimeIt(add)
def add(x, y):
    """ This is add"""
    time.sleep(2)
    return x + y

# 上下文用法
# with TimeIt(add) as foo:
#     print(foo(3,4))

print(add(5,6))
print(add.__doc__)
print(add.__dict__)
print(add.__name__)
print(type(add))

5a0cf72130e4d913bf000006
5a0cf74130e4d913bf000007

  • 第15行的用法wraps(fn)(self)是根據這個@wraps = wraps(fn)(wrapper)來的
    • @wraps是帶參裝飾器,fn就是帶參,wrapper是傳入的實參
    • 簡單來說,@wraps = wraps(fn)(wrapper)就是把fn的配置信息賦值給wrapper
    • 所以wraps(fn)(self)就是把fn的配置信息賦值給實例(self就是實例化后的實例)

本文來自投稿,不代表Linux運維部落立場,如若轉載,請注明出處:http://www.www58058.com/88584

(1)
nolannolan
上一篇 2017-11-18
下一篇 2017-11-18

相關推薦

  • nginx狀態監控

    通過查看Nginx的并發連接,我們可以更清除的知道網站的負載情況。Nginx并發查看有兩種方法(之所以這么說,是因為筆者只知道兩種),一種是通過web界面,一種是通過命令,web查看要比命令查看顯示的結果精確一些。下面介紹這兩種查看方法 No1、通過瀏覽器查看 通過web界面查看時Nginx需要開啟status模塊,也就是安裝Nginx時加上 &n…

    Linux干貨 2016-10-19
  • 馬哥教育網絡班22期+第6周課程練習 忍者亂太郎喻成

    請詳細總結vim編輯器的使用并完成以下練習題 1、復制/etc/rc.d/rc.sysinit文件至/tmp目錄,將/tmp/rc.sysinit文件中的以至少一個空白字符開頭的行的行首加#cp /etc/rc.d/rc.sysinit /tmp方法一 vim法在vim下用 : 進入命令行模式     %s/^&nbs…

    Linux干貨 2016-10-24
  • 私人定制—linux系統

    自制Linux系統: 1、分區并創建文件系統 [root@localhost6 ~]# fdisk  /dev/sdb 分兩個必要的分區 /dev/sdb1對應/boot /dev/sdb2對應根/ 創建文件系統: [root@localhost6 ~]# mkfs.ext4 /dev/s…

    Linux干貨 2016-09-26
  • linux的文件類型

    linux的文件類型 linux其中的一個哲學思想就是一切皆文件,今天我們就來學習一下linux系統中的文件類型。linux不同于windows,有多種多類的文件類型,文件類型的判斷也不是以文件的后綴為標準的。linux中一共有七種文件類型,分別是:普通文件、目錄、字符設備文件、塊設備文件、套接字文件、命名管道文件和符號鏈接文件。那么接下來我們就分別看看每種…

    Linux干貨 2016-10-27
  • lvs簡介

    一、 LVS簡介         LVS是Linux Virtual Server的簡稱,也就是Linux虛擬服務器, 是一個由章文嵩博士發起的自由軟件項目,它的官方站點是www.linuxvirtualserver.org?,F在LVS已經是 Linux標準內核的一部分,在Linux2.4…

    Linux干貨 2016-10-30
  • 馬哥教育網絡班20期+第5周課程練習

    1、顯示/boot/grub/grub.conf中以至少一個空白字符開頭的行; grep "^[[:space:]]\+.*" /boot/grub/grub.conf   2、顯示/etc/rc.d/rc.sysinit文件中以#開頭,后面跟至少一個空白字符,而后又有至少一個非空白字符的行; grep&nb…

    Linux干貨 2016-07-12
欧美性久久久久