{}const=>[]async()letfn</>var
開発

Pythonのコンテキストマネージャー: with、enter、exit

with演算子、enterメソッドとexitメソッドを分析し、独自のコンテキストマネージャーを作成し、リソースを安全に操作するための実用的な使用例を学びます。

К

Kodik

著者

2分で読める

ファイルを開いて、それを操作して、それを閉じるのを忘れる状況を想像してみてください。または、データベースへの接続を開くと、突然例外が発生し、リソースを解放する前にコードの実行が中断されます。聞き覚えがありますか? Pythonのコンテキストマネージャーは、この問題をエレガントかつ確実に解決します。

コンテキストマネージャーが必要な理由

コンテキストマネージャーは、リソースの自動管理を提供します。エラーが発生したかどうかに関係なく、リソースの開始前と終了後に特定のコードが実行されることを保証します。

コンテキストマネージャーなしの典型的な例:

file = open('data.txt', 'r')
try:
    content = file.read()
    # コンテンツの作業
finally:
    file.close()  # 閉めるのを忘れないでください!

コンテキストマネージャーを使用すると、すべてがはるかに簡単になります。

with open('data.txt', 'r') as file:
    content = file.read()
    # ブロックを終了すると、ファイルは自動的に閉じられます

🔥 10万人以上の学生が参加中

理論を読むのに疲れた?
コーディングの時間だ!

Kodik — 実践でプログラミングを学ぶアプリ。AIメンター、インタラクティブなレッスン、実際のプロジェクト。

🤖 AI 24時間
🎓 修了証
💰 無料
🚀 始める
今日参加

withの構造はどのように機能しますか?

Pythonが with演算子に遭遇すると、次のことが起こります。

  1. オブジェクトの __enter__() メソッドが呼び出されます

  2. 戻り値 __enter__() は、as の後の変数に割り当てられます (指定されている場合)

  3. withブロック内のコードを実行します

  4. ブロックの完了後(または例外が発生した場合)、__exit()メソッドが呼び出されます

これにより、ブロック内でエラーが発生した場合でも、リソースのクリーンアップが常に行われるようになります。

コンテキストマネージャーを作成する

独自のコンテキストマネージャーを作成するには、__enter__と__exit__という2つの特別なメソッドを実装する必要があります。

簡単な例:データベース接続マネージャー

class DatabaseConnection:
    def __init__(self, db_name):
        self.db_name = db_name
        self.connection = None
    
    def __enter__(self):
        print(f"Opening connection to {self.db_name}")
        self.connection = f"Connection to {self.db_name}"
        return self.connection
    
    def __exit__(self, exc_type, exc_value, traceback):
        print(f"Closing connection to {self.db_name}")
        if exc_type is not None:
            print(f"An error occurred: {exc_type.__name__}: {exc_value}")
        return False  # 例外はさらにスローされます

# 使用
with DatabaseConnection("users_db") as conn:
    print(f"We work with: {conn}")
    # ここにデータベースのコードを入力してください

結論:

Открываем соединение с users_db
Работаем с: Connection to users_db
Закрываем соединение с users_db

メソッド __enter ()

メソッド __enter__() は、コンテキストに入るときに呼び出されます。これは次のことができます。

  • リソースを初期化する

  • 接続を確立する

  • ロックをキャプチャする

  • 作業するオブジェクトを返す(asを介してアクセス可能)

def __enter__(self):
    # リソースの準備
    self.resource = self.acquire_resource()
    return self.resource  # この値は 'as' の後に変数に入ります

重要:メソッドは、selfを含む任意のオブジェクトを返すことができます。戻り値が不要な場合は、asなしでwithを使用できます。

メソッド __exit__ (exc_type, exc_value, traceback)

メソッド __exit__() は、コンテキストを終了するときに呼び出され、3つの引数を受け取ります。

  • exc_type — 例外の種類(例外がない場合はNone )

  • exc_value — 例外オブジェクト

  • traceback — 例外の traceback オブジェクト

戻り値は、例外を抑制するかどうかを決定します。

  • True — 例外は抑制されます(それ以上スローされません)

  • False または None — 例外はさらにスローされます

例外処理の例

class ErrorHandler:
    def __init__(self, suppress_errors=False):
        self.suppress_errors = suppress_errors
    
    def __enter__(self):
        print("Starting execution")
        return self
    
    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type is not None:
            print(f"Caught exception: {exc_type.__name__}")
            if self.suppress_errors:
                print("Exception suppressed")
                return True  # 例外を抑制する
        print("Completing")
        return False  # 例外をスローする

# 例外は抑制されます
with ErrorHandler(suppress_errors=True):
    print("Executing code")
    raise ValueError("Something went wrong!")
    print("This line will not be executed")

print("The program continues to work")

実用例

コード実行タイマー

import time

class Timer:
    def __enter__(self):
        self.start = time.time()
        return self
    
    def __exit__(self, *args):
        self.end = time.time()
        self.elapsed = self.end - self.start
        print(f"Execution time: {self.elapsed:.4f} seconds")

with Timer():
    # 実行時間を測定する
    total = sum(range(1000000))

ディレクトリの一時的な変更

import os

class ChangeDirectory:
    def __init__(self, new_path):
        self.new_path = new_path
        self.saved_path = None
    
    def __enter__(self):
        self.saved_path = os.getcwd()
        os.chdir(self.new_path)
        return self
    
    def __exit__(self, *args):
        os.chdir(self.saved_path)

with ChangeDirectory('/tmp'):
    print(f"Current directory: {os.getcwd()}")
    # /tmpで作業しています

print(f"Returned to: {os.getcwd()}")

トランザクション管理

class Transaction:
    def __init__(self, connection):
        self.connection = connection
    
    def __enter__(self):
        self.connection.begin()
        return self.connection
    
    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type is None:
            self.connection.commit()
            print("Transaction confirmed")
        else:
            self.connection.rollback()
            print("Transaction rolled back")
        return False

# 使用
# with Transaction(db_connection) as conn:
#     conn.execute("INSERT INTO users VALUES (...)")
# # エラーが発生した場合、変更は元に戻ります

contextlibモジュール—クラスなしのコンテキストマネージャー

Pythonにはcontextlibモジュールがあり、@contextmanagerデコレーターを使用してコンテキストマネージャーを簡単に作成できます。

from contextlib import contextmanager

@contextmanager
def managed_file(filename, mode):
    print(f"Opening file {filename}")
    file = open(filename, mode)
    try:
        yield file  # yield までのすべては __enter__ です
    finally:
        print(f"Closing file {filename}")
        file.close()  # yieldの後のすべては__exit__です

with managed_file('test.txt', 'w') as f:
    f.write('Hello, world!')

キーワードyieldはロジックを分割します。つまり、前のコードはコンテキストに入るときに実行され、後のコードはコンテキストを出るときに実行されます。

contextlib.suppressによる例外の抑制

from contextlib import suppress

# FileNotFoundErrorを無視する
with suppress(FileNotFoundError):
    os.remove('non-existent_file.txt')

print("The program continues to work")

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

Pythonでは、1つのwithステートメントで複数のコンテキストマネージャーを使用できます。

with open('input.txt', 'r') as infile, open('output.txt', 'w') as outfile:
    content = infile.read()
    outfile.write(content.upper())

これはネストされたブロックと同じです。

with open('input.txt', 'r') as infile:
    with open('output.txt', 'w') as outfile:
        content = infile.read()
        outfile.write(content.upper())

コンテキストマネージャーはいつ使用しますか?

コンテキストマネージャーは、「設定/クリア」のペア操作が必要な状況に最適です。

  • ファイルの操作

  • データベース接続の管理

  • マルチスレッドアプリケーションのロック

  • システムの状態の一時的な変更

  • トランザクション管理

  • 実行時間の測定

  • コードブロックへのログインとログアウト

ヒントとベストプラクティス

常に __exit__() でリソースを解放してください。例外が発生した場合でも、__exit__() メソッドが呼び出されるため、リソースを正しくクリーンアップする必要があります。

簡単なケースにはcontextlibを使用してください。クラスのすべての機能が必要ない場合は、@contextmanagerデコレーターを使用すると、コードが大幅に簡素化されます。

例外の抑制には注意してください。例外が正しく処理されていると確信している場合にのみ、__exit__()からTrueを返します。

コンテキストマネージャーは再利用できます。状態を正しく再初期化すれば、同じコンテキストマネージャーのオブジェクトを複数回使用できます。

例外の動作をテストします。コンテキストマネージャーが正常モードでもエラーが発生した場合でも正しく動作することを確認してください。

コンテキストマネージャーは、コードをよりクリーン、安全、そして信頼性の高いものにします。これにより、リソースを手動でクリーンアップする必要がなくなり、何か問題が発生した場合でも、すべてが正しく行われることが保証されます。これは、Pythonが開発者がより少ない労力でより良いコードを書くのにどのように役立つかの一例です。

アプリケーション コディック Python、JavaScript、HTML、CSS、その他のテクノロジーの初心者向けの構造化コースを提供します。トレーニングは、シンプルなものから複雑なものまで、徐々にマスターできるように構成されています。

私たちの Telegramチャンネル、コミュニティのサポートを見つけたり、質問をしたり、プログラミングの学習を支援したりすることができます。一緒に学ぶ方が簡単で楽しいです!

🎯先延ばしをやめよう

記事は気に入った?
実践の時間だ!

Kodikでは読むだけでなく、すぐにコードを書く。理論 + 実践 = 本当のスキル。

即座に実践
🧠AIがコードを説明
🏆修了証

登録不要 • カード不要