日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python关键字详解_Python 中的关键字with详解

發布時間:2023/12/2 python 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python关键字详解_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"

引用

總結

以上是生活随笔為你收集整理的python关键字详解_Python 中的关键字with详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。