ホームページ バックエンド開発 Python チュートリアル Python のコンテキスト マネージャー

Python のコンテキスト マネージャー

Mar 01, 2017 pm 02:16 PM

Python では、コード ブロックに入る前に __enter__ メソッドを呼び出し、コード ブロックから出た後に __exit__ メソッドを呼び出すオブジェクトがコンテキスト マネージャーとして使用されます。この記事では、Python のコンテキスト マネージャーを詳しく分析し、コンテキストマネージャー 機能と使用法:

1. コンテキストマネージャーとは何ですか?

たとえば、Python コードを書くとき、一連の操作をステートメント ブロックに入れることがよくあります:

(1) 特定の条件が true の場合 – このステートメント ブロックを実行します

(2) 特定の条件が true の場合true True – このステートメント ブロックをループします

場合によっては、プログラムがステートメント ブロックで実行されている間は特定の状態を維持し、ステートメント ブロックを終了した後にこの状態を終了する必要があります。

つまり、実際、コンテキスト マネージャーのタスクは、コード ブロックが実行される前に準備し、コード ブロックが実行された後にクリーンアップすることです。

コンテキスト マネージャーは Python 2.5 で追加された機能で、コードをより読みやすくし、エラーを減らすことができます。次に使い方を見ていきましょう。

2. コンテキストマネージャーの使用方法?

コードを見るのが最良の学習方法です。私たちが通常どのようにファイルを開いて「Hello World」を書くのかを見てみましょう。

filename = 'my_file.txt'
mode = 'w' # Mode that allows to write to the file
writer = open(filename, mode)
writer.write('Hello ')
writer.write('World')
writer.close()
ログイン後にコピー

1〜2行目では、ファイル名とその開き方(書き込み)を指定しています。

3 行目でファイルを開き、4 ~ 5 行目で「Hello world」と書き込み、6 行目でファイルを閉じます。

これで十分でない場合、なぜコンテキスト マネージャーが必要なのでしょうか?しかし、小さいながらも重要な詳細を見落としていました。ファイルを閉じるために 6 行目に到達しなかったらどうなるでしょうか?

たとえば、ディスクがいっぱいであるため、4 行目でファイルに書き込もうとすると例外がスローされ、6 行目はまったく実行できません。

もちろん、パッケージ化には try-finally ステートメント ブロックを使用できます:

writer = open(filename, mode)
try:
  writer.write('Hello ')
  writer.write('World')
finally:
  writer.close()
ログイン後にコピー

finally ステートメント ブロック内のコードは、try ステートメント ブロック内で何が起こっても実行されます。したがって、ファイルが閉じられることが保証されます。これを行うことに問題はありますか?もちろんそうではありませんが、「Hello world」を書くよりも複雑なことを行う場合、try-finally ステートメントは醜くなる可能性があります。たとえば、2 つのファイル (1 つは読み取り用、もう 1 つは書き込み用) を開き、2 つのファイル間でコピー操作を実行する場合、with ステートメントを使用すると、両方を同時に閉じることができます。

OK、内容を詳しく見てみましょう:

(1) まず、「writer」という名前のファイル変数を作成します。

(2) 次に、ライター上でいくつかの操作を実行します。

(3) 最後にライターを閉じます。

こっちの方がエレガントじゃないですか?

with open(filename, mode) as writer:
  writer.write('Hello ') 
  writer.write('World')
ログイン後にコピー

もう少し詳しく見てみましょう。「with」は新しいキーワードであり、コンテキスト マネージャーとともに常に表示されます。前のコードでは「open(filename, mode)」が登場しました。 「as」は、「open」関数から返されたコンテンツを参照し、それを新しい変数に割り当てるもう 1 つのキーワードです。 「writer」は新しい変数名です。

行 2 ~ 3、インデントして新しいコード ブロックを開きます。このコード ブロックでは、ライター上であらゆる操作を実行できます。このようにして「オープン」コンテキスト マネージャーを使用することで、コードがエレガントかつ安全であることが保証されます。 try-finally タスクを見事に実行します。

open 関数は、単純な関数としてもコンテキスト マネージャーとしても使用できます。これは、open 関数がファイル タイプ変数を返し、このファイル タイプが前に使用した write メソッドを実装しているためですが、これをコンテキスト マネージャーとして使用したい場合は、いくつかの特別なメソッドも実装する必要があります。これについては次に説明します。の項で紹介されています。

3. カスタム コンテキスト マネージャー

「オープン」コンテキスト マネージャーを作成しましょう。

コンテキスト マネージャーを実装するには、2 つのメソッドを実装する必要があります。1 つはステートメント ブロックに入るときの準備操作を担当し、もう 1 つはステートメント ブロックを出るときのアフターマス操作を担当します。同時に、ファイル名と開く方法という 2 つのパラメータが必要です。

Python クラスには、__enter__ と __exit__ (プレフィックスとサフィックスとして二重アンダースコア) という名前の 2 つの特別なメソッドが含まれています。

オブジェクトがコンテキストマネージャーとして使用される場合:

(1) コードブロックに入る前に __enter__ メソッドが呼び出されます。

(2) __exit__ メソッドは、コード ブロックを離れた後に呼び出されます (コード ブロック内で例外が発生した場合でも)。

以下は、コードブロックに出入りするときに出力するコンテキストマネージャーの例です。

class PypixContextManagerDemo:
 
  def __enter__(self):
    print 'Entering the block'
 
  def __exit__(self, *unused):
    print 'Exiting the block'
 
with PypixContextManagerDemo():
  print 'In the block'
 
#Output:
#Entering the block
#In the block
#Exiting the block
ログイン後にコピー

注意してください:

(1) はパラメーターを渡しません。
(2) ここでは「as」キーワードは使用されていません。
__exit__メソッドのパラメータ設定については後ほど説明します。
クラスにパラメータを渡すにはどうすればよいですか?実際、どのクラスでも __init__ メソッドを使用できます。ここでは、2 つの必要なパラメータ (ファイル名、モード) を受け取るようにメソッドを書き換えます。

ステートメントブロックに入ると、最初の例と同様に、open 関数が使用されます。そして、ステートメント ブロックを離れると、__enter__ 関数で開かれたすべてが閉じられます。

以下は私たちのコードです:

class PypixOpen:
 
  def __init__(self, filename, mode):
    self.filename = filename
    self.mode = mode
 
  def __enter__(self):
    self.openedFile = open(self.filename, self.mode)
    return self.openedFile
 
  def __exit__(self, *unused):
    self.openedFile.close()
 
with PypixOpen(filename, mode) as writer:
  writer.write("Hello World from our new Context Manager!")
ログイン後にコピー

変更点を見てみましょう:

(1) 行 3 ~ 5、2 つのパラメータは __init__ を通じて受信されます。

(2) 7〜9行目、ファイルを開いて戻ります。

(3)12行,当离开语句块时关闭文件。

(4)14-15行,模仿open使用我们自己的上下文管理器。

除此之外,还有一些需要强调的事情:

4.如何处理异常

我们完全忽视了语句块内部可能出现的问题。

如果语句块内部发生了异常,__exit__方法将被调用,而异常将会被重新抛出(re-raised)。当处理文件写入操作时,大部分时间你肯定不希望隐藏这些异常,所以这是可以的。而对于不希望重新抛出的异常,我们可以让__exit__方法简单的返回True来忽略语句块中发生的所有异常(大部分情况下这都不是明智之举)。

我们可以在异常发生时了解到更多详细的信息,完备的__exit__函数签名应该是这样的:

def __exit__(self, exc_type, exc_val, exc_tb)
ログイン後にコピー

这样__exit__函数就能够拿到关于异常的所有信息(异常类型,异常值以及异常追踪信息),这些信息将帮助异常处理操作。在这里我将不会详细讨论异常处理该如何写,以下是一个示例,只负责抛出SyntaxErrors异常。

class RaiseOnlyIfSyntaxError:
 
  def __enter__(self):
    pass
 
  def __exit__(self, exc_type, exc_val, exc_tb):
    return SyntaxError != exc_type
ログイン後にコピー


捕获异常:
当一个异常在with块中抛出时,它作为参数传递给__exit__。三个参数被使用,和sys.exc_info()返回的相同:类型、值和回溯(traceback)。当没有异常抛出时,三个参数都是None。上下文管理器可以通过从__exit__返回一个真(True)值来“吞下”异常。例外可以轻易忽略,因为如果__exit__不使用return直接结束,返回None——一个假(False)值,之后在__exit__结束后重新抛出。

捕获异常的能力创造了有意思的可能性。一个来自单元测试的经典例子——我们想确保一些代码抛出正确种类的异常:

class assert_raises(object):
  # based on pytest and unittest.TestCase
  def __init__(self, type):
    self.type = type
  def __enter__(self):
    pass
  def __exit__(self, type, value, traceback):
    if type is None:
      raise AssertionError('exception expected')
    if issubclass(type, self.type):
      return True # swallow the expected exception
    raise AssertionError('wrong exception type')

with assert_raises(KeyError):
  {}['foo']
ログイン後にコピー

5. 谈一些关于上下文库(contextlib)的内容

contextlib是一个Python模块,作用是提供更易用的上下文管理器。

(1)contextlib.closing

假设我们有一个创建数据库函数,它将返回一个数据库对象,并且在使用完之后关闭相关资源(数据库连接会话等)

我们可以像以往那样处理或是通过上下文管理器:

with contextlib.closing(CreateDatabase()) as database:
  database.query()
ログイン後にコピー

contextlib.closing方法将在语句块结束后调用数据库的关闭方法。

(2)contextlib.nested

另一个很cool的特性能够有效地帮助我们减少嵌套:

假设我们有两个文件,一个读一个写,需要进行拷贝。

以下是不提倡的:

with open('toReadFile', 'r') as reader:
  with open('toWriteFile', 'w') as writer:
    writer.writer(reader.read())
ログイン後にコピー

可以通过contextlib.nested进行简化:

with contextlib.nested(open('fileToRead.txt', 'r'),
            open('fileToWrite.txt', 'w')) as (reader, writer):
  writer.write(reader.read())
ログイン後にコピー

在Python2.7中这种写法被一种新语法取代:

with open('fileToRead.txt', 'r') as reader, \
    open('fileToWrite.txt', 'w') as writer:
    writer.write(reader.read())
contextlib.contextmanager
ログイン後にコピー

对于Python高级玩家来说,任何能够被yield关键词分割成两部分的函数,都能够通过装饰器装饰的上下文管理器来实现。任何在yield之前的内容都可以看做在代码块执行前的操作,而任何yield之后的操作都可以放在exit函数中。

这里我举一个线程锁的例子:

下面是线程安全写函数的例子:

import threading
 
lock = threading.Lock()
 
def safeWriteToFile(openedFile, content):
  lock.acquire()
  openedFile.write(content)
  lock.release()
ログイン後にコピー

接下来,让我们用上下文管理器来实现,回想之前关于yield和contextlib的分析:

@contextlib.contextmanager
def loudLock():
  print 'Locking'
  lock.acquire()
  yield
  print 'Releasing'
  lock.release()
 
with loudLock():
  print 'Lock is locked: %s' % lock.locked()
  print 'Doing something that needs locking'
 
#Output:
#Locking
#Lock is locked: True
#Doing something that needs locking
#Releasing
ログイン後にコピー

特别注意,这不是异常安全(exception safe)的写法。如果你想保证异常安全,请对yield使用try语句。幸运的是threading。lock已经是一个上下文管理器了,所以我们只需要简单地:

@contextlib.contextmanager
def loudLock():
  print 'Locking'
  with lock:
    yield
  print 'Releasing'
ログイン後にコピー

因为threading.lock在异常发生时会通过__exit__函数返回False,这将在yield被调用是被重新抛出。这种情况下锁将被释放,但对于“print ‘Releasing'”的调用则不会被执行,除非我们重写try-finally。

如果你希望在上下文管理器中使用“as”关键字,那么就用yield返回你需要的值,它将通过as关键字赋值给新的变量。下面我们就仔细来讲一下。

6.使用生成器定义上下文管理器
当讨论生成器时,据说我们相比实现为类的迭代器更倾向于生成器,因为它们更短小方便,状态被局部保存而非实例和变量中。另一方面,正如双向通信章节描述的那样,生成器和它的调用者之间的数据流可以是双向的。包括异常,可以直接传递给生成器。我们想将上下文管理器实现为特殊的生成器函数。事实上,生成器协议被设计成支持这个用例。

@contextlib.contextmanager
def some_generator(<arguments>):
  <setup>
  try:
    yield <value>
  finally:
    <cleanup>
ログイン後にコピー

contextlib.contextmanager装饰一个生成器并转换为上下文管理器。生成器必须遵循一些被包装(wrapper)函数强制执行的法则——最重要的是它至少yield一次。yield之前的部分从__enter__执行,上下文管理器中的代码块当生成器停在yield时执行,剩下的在__exit__中执行。如果异常被抛出,解释器通过__exit__的参数将之传递给包装函数,包装函数于是在yield语句处抛出异常。通过使用生成器,上下文管理器变得更短小精炼。

让我们用生成器重写closing的例子:

@contextlib.contextmanager
def closing(obj):
  try:
    yield obj
  finally:
    obj.close()
ログイン後にコピー

再把assert_raises改写成生成器:

@contextlib.contextmanager
def assert_raises(type):
  try:
    yield
  except type:
    return
  except Exception as value:
    raise AssertionError(&#39;wrong exception type&#39;)
  else:
    raise AssertionError(&#39;exception expected&#39;)
ログイン後にコピー

这里我们用装饰器将生成函数转化为上下文管理器!

更多Python中的上下文管理器相关文章请关注PHP中文网!


このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

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

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

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

Pythonを使用してテキストファイルのZIPF配布を見つける方法 Pythonを使用してテキストファイルのZIPF配布を見つける方法 Mar 05, 2025 am 09:58 AM

このチュートリアルでは、Pythonを使用してZIPFの法則の統計的概念を処理する方法を示し、法律の処理時にPythonの読み取りおよび並べ替えの効率性を示します。 ZIPF分布という用語が何を意味するのか疑問に思うかもしれません。この用語を理解するには、まずZIPFの法律を定義する必要があります。心配しないでください、私は指示を簡素化しようとします。 ZIPFの法則 ZIPFの法則は単に意味します。大きな自然言語のコーパスでは、最も頻繁に発生する単語は、2番目の頻繁な単語のほぼ2倍の頻度で表示されます。 例を見てみましょう。アメリカ英語の茶色のコーパスを見ると、最も頻繁な言葉は「thであることに気付くでしょう。

HTMLを解析するために美しいスープを使用するにはどうすればよいですか? HTMLを解析するために美しいスープを使用するにはどうすればよいですか? Mar 10, 2025 pm 06:54 PM

この記事では、Pythonライブラリである美しいスープを使用してHTMLを解析する方法について説明します。 find()、find_all()、select()、およびget_text()などの一般的な方法は、データ抽出、多様なHTML構造とエラーの処理、および代替案(SEL

Pythonでファイルをダウンロードする方法 Pythonでファイルをダウンロードする方法 Mar 01, 2025 am 10:03 AM

Pythonは、インターネットからファイルをダウンロードするさまざまな方法を提供します。これは、urllibパッケージまたはリクエストライブラリを使用してHTTPを介してダウンロードできます。このチュートリアルでは、これらのライブラリを使用してPythonからURLからファイルをダウンロードする方法を説明します。 ライブラリをリクエストします リクエストは、Pythonで最も人気のあるライブラリの1つです。クエリ文字列をURLに手動で追加したり、POSTデータのエンコードをフォームに追加せずに、HTTP/1.1リクエストを送信できます。 リクエストライブラリは、以下を含む多くの機能を実行できます フォームデータを追加します マルチパートファイルを追加します Python応答データにアクセスします リクエストを行います 頭

Pythonでの画像フィルタリング Pythonでの画像フィルタリング Mar 03, 2025 am 09:44 AM

ノイズの多い画像を扱うことは、特に携帯電話や低解像度のカメラの写真でよくある問題です。 このチュートリアルでは、OpenCVを使用してPythonの画像フィルタリング手法を調査して、この問題に取り組みます。 画像フィルタリング:強力なツール 画像フィルター

Pythonを使用してPDFドキュメントの操作方法 Pythonを使用してPDFドキュメントの操作方法 Mar 02, 2025 am 09:54 AM

PDFファイルは、クロスプラットフォームの互換性に人気があり、オペレーティングシステム、読み取りデバイス、ソフトウェア間でコンテンツとレイアウトが一貫しています。ただし、Python Plansing Plain Text Filesとは異なり、PDFファイルは、より複雑な構造を持つバイナリファイルであり、フォント、色、画像などの要素を含んでいます。 幸いなことに、Pythonの外部モジュールでPDFファイルを処理することは難しくありません。この記事では、PYPDF2モジュールを使用して、PDFファイルを開き、ページを印刷し、テキストを抽出する方法を示します。 PDFファイルの作成と編集については、私からの別のチュートリアルを参照してください。 準備 コアは、外部モジュールPYPDF2を使用することにあります。まず、PIPを使用してインストールします。 ピップはpです

DjangoアプリケーションでRedisを使用してキャッシュする方法 DjangoアプリケーションでRedisを使用してキャッシュする方法 Mar 02, 2025 am 10:10 AM

このチュートリアルでは、Redisキャッシングを活用して、特にDjangoフレームワーク内でPythonアプリケーションのパフォーマンスを向上させる方法を示しています。 Redisのインストール、Django構成、およびパフォーマンスの比較をカバーして、Beneを強調します

Natural Language Toolkit(NLTK)の紹介 Natural Language Toolkit(NLTK)の紹介 Mar 01, 2025 am 10:05 AM

自然言語処理(NLP)は、人間の言語の自動または半自動処理です。 NLPは言語学と密接に関連しており、認知科学、心理学、生理学、数学の研究とのリンクがあります。コンピューターサイエンスで

TensorflowまたはPytorchで深い学習を実行する方法は? TensorflowまたはPytorchで深い学習を実行する方法は? Mar 10, 2025 pm 06:52 PM

この記事では、深い学習のためにTensorflowとPytorchを比較しています。 関連する手順、データの準備、モデルの構築、トレーニング、評価、展開について詳しく説明しています。 特に計算グラップに関して、フレームワーク間の重要な違い

See all articles