python的with語法的深刻理解

若是你有一些對象(好比一個文件、網絡鏈接或鎖),須要支持 with 語句,下面介紹兩種定義方法。python

方法(1):網絡

首先介紹下with 工做原理
(1)緊跟with後面的語句被求值後,返回對象的「__enter__()」方法被調用,這個方法的返回值將被賦值給as後面的變量;
(2)當with後面的代碼塊所有被執行完以後,將調用前面返回對象的「__exit__()」方法。函數

with工做原理代碼示例:this

class Sample:
    def __enter__(self):
        print "in __enter__"
        return "Foo"
    def __exit__(self, exc_type, exc_val, exc_tb):
        print "in __exit__"
def get_sample():
    return Sample()
with get_sample() as sample:
    print "Sample: ", sample

  代碼的運行結果以下:code

in __enter__
Sample:  Foo
in __exit__

  

能夠看到,整個運行過程以下:
(1)enter()方法被執行;
(2)enter()方法的返回值,在這個例子中是」Foo」,賦值給變量sample;
(3)執行代碼塊,打印sample變量的值爲」Foo」;
(4)exit()方法被調用;orm

【注:】exit()方法中有3個參數, exc_type, exc_val, exc_tb,這些參數在異常處理中至關有用。
exc_type: 錯誤的類型
exc_val: 錯誤類型對應的值
exc_tb: 代碼中錯誤發生的位置
示例代碼:對象

class Sample():
    def __enter__(self):
        print('in enter')
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print "type: ", exc_type
        print "val: ", exc_val
        print "tb: ", exc_tb
    def do_something(self):
        bar = 1 / 0
        return bar + 10
with Sample() as sample:
    sample.do_something()

  程序輸出結果:blog

in enter
Traceback (most recent call last):
type:  <type 'exceptions.ZeroDivisionError'>
val:  integer division or modulo by zero
  File "/home/user/cltdevelop/Code/TF_Practice_2017_06_06/with_test.py", line 36, in <module>
tb:  <traceback object at 0x7f9e13fc6050>
    sample.do_something()
  File "/home/user/cltdevelop/Code/TF_Practice_2017_06_06/with_test.py", line 32, in do_something
    bar = 1 / 0
ZeroDivisionError: integer division or modulo by zero

Process finished with exit code 1

  

方法(2):get

實現一個新的上下文管理器的最簡單的方法就是使用 contexlib 模塊中的 @contextmanager 裝飾器。 下面是一個實現了代碼塊計時功能的上下文管理器例子:it

import time
from contextlib import contextmanager

@contextmanager
def timethis(label):
    start = time.time()
    try:
        yield
    finally:
        end = time.time()
        print('{}: {}'.format(label, end - start))

  

# Example use
with timethis('counting'):
    n = 10000000
    while n > 0:
        n -= 1

  

在函數 timethis() 中,yield 以前的代碼會在上下文管理器中做爲 __enter__() 方法執行, 全部在 yield 以後的代碼會做爲 __exit__() 方法執行。 若是出現了異常,異常會在yield語句那裏拋出。

【注】 一、@contextmanager 應該僅僅用來寫自包含的上下文管理函數。 二、若是你有一些對象(好比一個文件、網絡鏈接或鎖),須要支持 with 語句,那麼你就須要單獨實現 __enter__() 方法和 __exit__() 方法。

相關文章
相關標籤/搜索