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

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

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

裝飾器實現

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 15:14
下一篇 2017-11-18 19:54

相關推薦

  • 懶人本地YUM源設置

    一、掛載本地光盤到根目錄下/media。 [root@wing-PC ~]# mount -v /dev/sr0 /media/ 二、用mv 修改/etc/yum.repos.d下面的文件。 [root@wing-PC ~]# mv -v /etc/yum.repos.d/CentOS-Base.repo / CentOS-Base.repo.bak [ro…

    系統運維 2017-08-05
  • vim編輯器作業

    1、復制/etc/profile至/tmp/目錄,用查找替換命令刪除/tmp/profile文件中的行首的空白字符 [root@wzc tmp]# vim profile    # By default, we want umask to …

    Linux干貨 2016-08-12
  • N22第七周作業

    1.創建一個10G的分區,并格式為ext4的文件系統;     (1)要求其block大小為2048,預留空間百分比為2,卷標為MYDATA,默認掛載屬性包含acl; # fdisk /dev/sdb 歡迎使用 fdisk (util-linux 2.23.2)。 更改…

    Linux干貨 2016-10-24
  • 創建yum源及httpd源碼編譯

    創建yum源及源碼編譯httpd yum本身相比于rpm來說,能夠將有依賴的包文件一次性的安裝完成,是相當的方便的。 yum的服務器支持的幾種格式: http、https、ftp、file 1、yum基礎命令 1、yum命令 yum [options] [command] [package …] [options]: 基本不用 [command]: re…

    Linux干貨 2017-08-08
  • 20160805作業-正則表達式

    http://note.youdao.com/yws/public/redirect/share?id=1e40165dc16523400e14433e71683963&type=false

    Linux干貨 2016-08-08
  • shell腳本編程入門

    什么是shell腳本,其實,shell腳本就是利用shell的功能所寫的一個程序,這個程序是使用純文本文件,將一些shell的語法與命令(包含外部命令)寫在里面,搭配正則表達式、管道命令與數據流重定向等功能等這些命令的組合起來,以達到我們所想要的目的。 程序編程風格有兩種: 過程式:以指令為中心,數據服務于指令。 對象式:以數據為中心,指令服務于數據。 過程…

    Linux干貨 2016-08-18
欧美性久久久久