Pythonのwith文を詳しく解説
Jun 16, 2016 am 08:43 AM一、简介
with是从Python 2.5 引入的一个新的语法,更准确的说,是一种上下文的管理协议,用于简化try…except…finally的处理流程。with通过__enter__方法初始化,然后在__exit__中做善后以及处理异常。对于一些需要预先设置,事后要清理的一些任务,with提供了一种非常方便的表达。
with的基本语法如下,EXPR是一个任意表达式,VAR是一个单一的变量(可以是tuple),”as VAR”是可选的。
with EXPR as VAR:
BLOCK
根据PEP 343的解释,with…as…会被翻译成以下语句:
mgr = (EXPR)
exit = type(mgr).__exit__ # Not calling it yet
value = type(mgr).__enter__(mgr)
exc = True
try:
try:
VAR = value # Only if "as VAR" is present
BLOCK
except:
# The exceptional case is handled here
exc = False
if not exit(mgr, *sys.exc_info()):
raise
# The exception is swallowed if exit() returns true
finally:
# The normal and non-local-goto cases are handled here
if exc:
exit(mgr, None, None, None)
为什么这么复杂呢?注意finally中的代码,需要BLOCK被执行后才会执行finally的清理工作,因为当EXPR执行时抛出异常,访问mgr.exit执行就会报AttributeError的错误。
二、实现方式
根据前面对with的翻译可以看到,被with求值的对象必须有一个__enter__方法和一个__exit__方法。稍微看一个文件读取的例子吧,注意在这里我们要解决2个问题:文件读取异常,读取完毕后关闭文件句柄。用try…except一般会这样写:
f = open('/tmp/tmp.txt')
try:
for line in f.readlines():
print(line)
finally:
f.close()
注意我们这里没有处理文件打开失败的IOError,上面的写法可以正常工作,但是对于每个打开的文件,我们都要手动关闭文件句柄。如果要使用with来实现上述功能,需要需要一个代理类:
class opened(object):
def __init__(self, name):
self.handle = open(name)
def __enter__(self):
return self.handle
def __exit__(self, type, value, trackback):
self.handle.close()
with opened('/tmp/a.txt') as f:
for line in f.readlines():
print(line)
注意我们定了一个名字叫opened的辅助类,并实现了__enter__和__exit__方法,__enter__方法没有参数,__exit__方法的3个参数,分别代表异常的类型、值、以及堆栈信息,如果没有异常,3个入参的值都为None。
如果你不喜欢定义class,还可以用Python标准库提供的contextlib来实现:
from contextlib import contextmanager
@contextmanager
def opened(name):
f = open(name)
try:
yield f
finally:
f.close()
with opened('/tmp/a.txt') as f:
for line in f.readlines():
print(line)
使用contextmanager的函数,yield只能返回一个参数,而yield后面是处理清理工作的代码。在我们读取文件的例子中,就是关闭文件句柄。这里原理上和我们之前实现的类opened是相同的,有兴趣的可以参考一下contextmanager的源代码。
3. アプリケーションのシナリオ
あまりにもナンセンスなので、どのようなシナリオで使用すべきでしょうか?もちろん、そうでなければこの記事の意味はありません。以下は PEP 343 からの抜粋です。
実行前にコードがロックされ、実行後にロックが解放されることを保証するテンプレート:
@contextmanager
def locked(lock):
lock.acquire()
try:
yield
Final:
lock.release()
with locked(myLock):
# ここのコードは myLock が保持された状態で実行されます。ロックは
# ブロックが離れると解放されることが保証されます (
# リターン経由またはキャッチされなかった例外の場合) ).
データベース トランザクションのコミットとロールバック:
@contextmanager
deftransaction(db):
db.begin()
try:
yield None
例外:
db.rollback()
raise
else :
db.commit()
標準出力をリダイレクト:
@ contextmanager
def stdout_redirected(new_stdout):
save_stdout = sys.stdout
sys.stdout = new_stdout
try:
yield None
finally:
sys. stdout = save_stdout
with opens(filename, "w") as f:
with stdout_redirected(f):
print "Hello world"
上記の例はスレッドセーフではないことに注意してください。スレッド数に関係なく、環境には注意してください。
4. 概要
With は、try...expect...finally 構文を簡略化したもので、例外を処理するための非常に優れた方法を提供します。 Python の構文を使用して実装するには、クラスベースとデコレータベースの 2 つの方法があり、これら 2 つの方法は原則として同等であり、特定のシナリオに応じて選択できます。
with はもともとブロック...as... 構文から生まれましたが、この構文は多くの人によって反対され、最終的に with が誕生しました。この歴史については、現在でも PEP-343 と PEP-340 を参照してください。 🎜>

人気の記事

人気の記事

ホットな記事タグ

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック











Google AI、開発者向けに Gemini 1.5 Pro と Gemma 2 を発表

わずか 250 ドルで、Hugging Face のテクニカル ディレクターが Llama 3 を段階的に微調整する方法を教えます

いくつかの .NET オープンソース AI および LLM 関連プロジェクト フレームワークを共有する
