python中為什么推薦使用with_Python中的with關鍵字使用詳解

這篇文章主要介紹了Python 中的with關鍵字使用詳解的相關資料,在Python中,with關鍵字是一個替你管理實現上下文協議對象的好東西,需要的朋友可以參考下

">

在 Python 2.5 中, with 關鍵字被加入。它將常用的 try ... except ... finally ... 模式很方便的被復用。看一個最經典的例子:with open('file.txt') as f:

content = f.read()

在這段代碼中,無論 with 中的代碼塊在執行的過程中發生任何情況,文件最終都會被關閉。如果代碼塊在執行的過程中發生了一個異常,那么在這個異常被拋出前,程序會先將被打開的文件關閉。

再看另外一個例子。

在發起一個數據庫事務請求的時候,經常會用類似這樣的代碼:db.begin()

try:

# do some actions

except:

db.rollback()

raise

finally:

db.commit()

如果將發起事務請求的操作變成可以支持 with 關鍵字的,那么用像這樣的代碼就可以了:with transaction(db):

# do some actions

下面,詳細的說明一下 with 的執行過程,并用兩種常用的方式實現上面的代碼。

with 的一般執行過程

一段基本的 with 表達式,其結構是這樣的:with EXPR as VAR:

BLOCK

其中: EXPR 可以是任意表達式; as VAR 是可選的。其一般的執行過程是這樣的:計算 EXPR ,并獲取一個上下文管理器。

上下文管理器的 exit() 方法被保存起來用于之后的調用。

調用上下文管理器的 enter() 方法。

如果 with 表達式包含 as VAR ,那么 EXPR 的返回值被賦值給 VAR 。

執行 BLOCK 中的表達式。

調用上下文管理器的 exit() 方法。如果 BLOCK 的執行過程中發生了一個異常導致程序退出,那么異常的 type 、 value 和 traceback (即 sys.exc_info()的返回值 )將作為參數傳遞給 exit() 方法。否則,將傳遞三個 None 。

將這個過程用代碼表示,是這樣的:mgr = (EXPR)

exit = type(mgr).exit # 這里沒有執行

value = type(mgr).enter(mgr)

exc = True

try:

try:

VAR = value # 如果有 as VAR

BLOCK

except:

exc = False

if not exit(mgr, *sys.exc_info()):

raise

finally:

if exc:

exit(mgr, None, None, None)

這個過程有幾個細節:

如果上下文管理器中沒有 enter() 或者 exit() 中的任意一個方法,那么解釋器會拋出一個 AttributeError 。

在 BLOCK 中發生異常后,如果 exit() 方法返回一個可被看成是 True 的值,那么這個異常就不會被拋出,后面的代碼會繼續執行。

接下來,用兩種方法來實現上面來實現上面的過程的吧。

實現上下文管理器類

第一種方法是實現一個類,其含有一個實例屬性 db 和上下文管理器所需要的方法 enter() 和 exit() 。class transaction(object):

def init(self, db):

self.db = db

def enter(self):

self.db.begin()

def exit(self, type, value, traceback):

if type is None:

db.commit()

else:

db.rollback()

了解 with 的執行過程后,這個實現方式是很容易理解的。下面介紹的實現方式,其原理理解起來要復雜很多。

使用生成器裝飾器

在Python的標準庫中,有一個裝飾器可以通過生成器獲取上下文管理器。使用生成器裝飾器的實現過程如下:from contextlib import contextmanager

@contextmanager

def transaction(db):

db.begin()

try:

yield db

except:

db.rollback()

raise

else:

db.commit()

第一眼上看去,這種實現方式更為簡單,但是其機制更為復雜。看一下其執行過程吧:Python解釋器識別到 yield 關鍵字后, def 會創建一個生成器函數替代常規的函數(在類定義之外我喜歡用函數代替方法)。

裝飾器 contextmanager 被調用并返回一個幫助方法,這個幫助函數在被調用后會生成一個 GeneratorContextManager 實例。最終 with 表達式中的 EXPR 調用的是由 contentmanager 裝飾器返回的幫助函數。

with 表達式調用 transaction(db) ,實際上是調用幫助函數。幫助函數調用生成器函數,生成器函數創建一個生成器。

幫助函數將這個生成器傳遞給 GeneratorContextManager ,并創建一個 GeneratorContextManager 的實例對象作為上下文管理器。

with 表達式調用實例對象的上下文管理器的 enter() 方法。

enter() 方法中會調用這個生成器的 next() 方法。這時候,生成器方法會執行到 yield db 處停止,并將 db 作為 next() 的返回值。如果有 as VAR ,那么它將會被賦值給 VAR 。

with 中的 BLOCK 被執行。

BLOCK 執行結束后,調用上下文管理器的 exit() 方法。 exit() 方法會再次調用生成器的 next() 方法。如果發生 StopIteration 異常,則 pass 。

如果沒有發生異常生成器方法將會執行 db.commit() ,否則會執行 db.rollback() 。

再次看看上述過程的代碼大致實現:def contextmanager(func):

def helper(*args, **kwargs):

return GeneratorContextManager(func(*args, **kwargs))

return helper

class GeneratorContextManager(object):

def init(self, gen):

self.gen = gen

def enter(self):

try:

return self.gen.next()

except StopIteration:

raise RuntimeError("generator didn't yield")

def exit(self, type, value, traceback):

if type is None:

try:

self.gen.next()

except StopIteration:

pass

else:

raise RuntimeError("generator didn't stop")

else:

try:

self.gen.throw(type, value, traceback)

raise RuntimeError("generator didn't stop after throw()")

except StopIteration:

return True

except:

if sys.exc_info()[1] is not value:

raise

總結

Python的 with 表達式包含了很多Python特性。花點時間吃透 with 是一件非常值得的事情。

一些其他的例子

鎖機制@contextmanager

def locked(lock):

lock.acquired()

try:

yield

finally:

lock.release()

標準輸出重定向@contextmanager

def stdout_redirect(new_stdout):

old_stdout = sys.stdout

sys.stdout = new_stdout

try:

yield

finally:

sys.stdout = old_stdout

with open("file.txt", "w") as f:

with stdout_redirect(f):

print "hello world"

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/539751.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/539751.shtml
英文地址,請注明出處:http://en.pswp.cn/news/539751.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

create table as select性能測試

轉載自:http://blog.csdn.net/yangzhijun_cau/article/details/7396088 --------------------------------------------------------------------------------- 原表270W數據,無照片,字段比較多,有50個左右 測試機是一個虛擬機&a…

類似索引Model套Model之 iOS模型閑聊二

看下界面, 這是類似于索引的頁面, 只不過木有右側索引條的布局. 如果想了解通訊錄索引的,請移步iOS - 高仿通訊錄之商品索引排序搜索. 提供思路如下: 分析界面及接口用 MVC 設計模式來實現(其實核心點都在下面5)創建內外層 Model 并綁定兩者 Model兩者 Cell 布局的實現 (便于后…

輸入法畫面_搜狗輸入法:用AI技術譜寫詩意生活

十九世紀著名的思想家斯賓塞曾說:科學本身就富有詩意。這里應該包含兩種意思,字面上,科學是飽含文字之美的,比如原理和規律的推演,僅通過文字符號的簡單排列,便有了生機。但深層次上科學又不止于文字&#…

hadoop偽分布式(單機版)安裝,Linux

一、下載 1、hadoop官網下載:https://archive.apache.org/dist/hadoop/common/ 進入stable文件夾里下載,這是穩定版本。 stable/ 本文的版本是 hadoop-2.7.2.tar.gz 2、jdk下載,JDK7及以上,本文用jdk8-64位 二、版本區別 2.…

c++-add two numbers 兩個鏈表相加

題目描述 You are given two linked lists representing two non-negative numbers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list. Input: (2 -> 4 -> 3) (5 -> 6…

城軌的兩類時鐘系統均同步于_基于兩臺SDS3000示波器同步產生“8通道”示波器...

在很多應用場合需要4通道以上的示波器,但是市面上極大部分示波器最多只有四通道,而且沒有外部輸入的同步時鐘接口。 有什么快捷的方法獲得更多通道功能的示波器? 最簡便的方法是:將兩臺示波器的輔助輸入信號作為觸發源,同時連接到…

Linux設置ssh免密碼登錄

一、SSH來源 對于需要遠程管理其它機器,一般使用遠程桌面或者telnet。linux一般只能是telnet。但是telnet的缺點是通信不加密,存在不安全因素,只適合內網訪問。 為解決這個問題,推出了通信加密通信協議,即SSH&#x…

解析json數據_Retrofit同時解析JSON和XML數據格式

前言Android開發中,我們會經常遇到前端需要解析兩種數據格式(json和xml),比如自己服務器返回的是json格式的數據,我們做微信登錄的時候,微信返回的格式又是xml格式的。我們可以通過自己編寫Retrofit的ConverterFactory來做到可以同時解析兩種…

ORACLE 小時值必須介于1和12之間 解決方法

ORACLE數據庫查詢語句: "select * from dual where time>to_date(2012-10-29 19:45:34,yyyy-mm-dd HH:mi:ss)"當執行時,會拋出錯誤:ORA-01849: 小時值必須介于 1 和 12 之間 01849. 00000 - "hour must be between 1 and 1…

jenkins+svn+maven+ssh 部署配置詳細記錄

2019獨角獸企業重金招聘Python工程師標準>>> 先簡單記錄一下,后面再慢慢完善。 1、環境 jdk 1.7.0_45 maven 3.1.1 jenkins 2.3.21 jdk和maven的安裝就不必多說了,主要是jenkins的安裝需要說下,jenkins有war包和yum還有rpm等安裝方…

k8s安裝sqlite3_kubernetes環境部署單節點redis數據庫的方法

kubernetes部署redis數據庫(單節點)redis簡介Redis 是我們常用的非關系型數據庫,在項目開發、測試、部署到生成環境時,經常需要部署一套 Redis 來對數據進行緩存。這里介紹下如何在 Kubernetes 環境中部署用于開發、測試的環境的 Redis 數據庫&#xff0…

oracle 都是parallel惹的禍【1-2分鐘出結果變1-2秒】

原文:http://blog.csdn.net/shushugood/article/details/9000628 -------------------------------------------------------- 該項目是中國聯通xxxx話務系統,我的架構設計需求設計,運維保障數據庫開發,全套服務。 在今天開發完畢…

二叉搜索樹(BST樹)的簡單實現

#include <stdlib.h>template<typename T>class CBinSTree;template <typename T>class CTreeNode{//樹節點類public:CTreeNode(const T& item,CTreeNode<T>* lptr NULL,CTreeNode<T>* rptr NULL):data(item),left(lptr),right(rptr){}CTr…

Oracle 創建 DBLink 的方法

原文出處&#xff1a;http://blog.csdn.net/davidhsing/article/details/6408770 ------------------- 1、如果需要創建全局 DBLink&#xff0c;則需要先確定用戶有創建 dblink 的權限&#xff1a; [c-sharp] view plaincopy print?select * from user_sys_privs where privi…

eclipse init 配置

--設置最大的堆和最小堆大小.兩者一樣表示固定大小.這樣可以防止老年代內存擴展造成額外的gc.當然也會多占一些內存.系統內存不足的慎用 -Xms512m -Xmx512m --加大年輕代內存.減少minor gc -Xmn164m --這個是永久代大小.默認是64M,增加到96M.固定大小,減少擴展造成的gc -XX:Per…

Oracle對表空間操作的sql

管理員給用戶增加不限制表空間權限 grant unlimited tablespace to 用戶; 查看表空間使用情況 SELECT a.tablespace_name "表空間名", total "表空間大小", free "表空間剩余大小", (total - free) "表空間使用大小", total / (…

IPKISS Tutorials------線路仿真

IPKISS------線路仿真 推薦閱讀引言正文示例1------PDK中集成好的器件示例2------使用 i3.Circuit 框架示例3------i3.PCell 框架推薦閱讀 Matplotlib ------ 縱坐標科學計數法含義 引言 我們知道,想要在 IPKISS 中進行仿真,首先需要對線路進行定義,但是我們知道,在 IPK…

Oracle Database 11g Express Edition使用限制,與其他版本的區別

Oracle Database 11g Express Edition是 Oracle 數據庫的免費版本&#xff0c;支持標準版的大部分功能&#xff0c;11g Express Edition 提供 Windows 和 Linux 版本。 做為免費的 Oracle 數據庫版本&#xff0c;Express Edition的限制是&#xff1a; 1&#xff09;最大數據庫大…

c++ 復制構造函數_C++學習刷題8--復制構造函數和賦值運算符重載函數

一、前言本部分為C語言刷題系列中的第8節&#xff0c;主要講解這幾個知識點&#xff1a;復制構造函數和賦值運算符重載函數。歡迎大家提出意見、指出錯誤或提供更好的題目&#xff01;二、知識點講解知識點1&#xff1a;復制構造函數1、當依據一個已存對象創建一個新對象時&…

ORACLE使用WITH AS和HINT MATERIALIZE優化SQL解決FILTER效率低下

原文&#xff1a;http://blog.csdn.net/liangweiwei130/article/details/37882503 ------------------------------------------------- 在做項目的過程中&#xff0c;一個頁面使用類似如下的SQL查詢數據&#xff0c;為了保密和使用方便&#xff0c;我把項目中有關的表名和字段…