いろは3010814 views
高校化学2924392 views
数学講師2886227 views
小学算数1200427 views
世界の国564520 views
LaTeX962153 views
教育149510 views
英語613639 views
高校生物551766 views
りんご209490 views
Help
Tools
NewsSpreadsheetCalendarSlidesTier ListPen ToolIllustrationCrayonPixel ArtASCII ArtPerspectiveEndless StairsGraphMind MapER DiagramFamily TreeMemeCurved TextImage EditorMosaicRetro FilterPencil SketchSwirl EffectOCR/HighlighterMakeup EditorFaviconVideo TrimmerScrolling VideoVideo TitleColor PickerColor ExtractorBonfireFireworksWater RippleWater SplashBreaking GlassWood GrainMarble TextureCSS ButtonIcon MakerBar ChartGrouped Bar ChartStacked Bar ChartPie ChartLine ChartArea ChartStacked Area ChartScatter Plot3D Bar Chart3D Pie ChartBar Chart RaceBubble ChartPopulation PyramidPictogramEarningsCandlestick ChartInvestment RiskMortgage SimulatorCalculatorMatrix CalculatorFunction GraphPolynomial ExpansionVenn DiagramField VisualizerRubik's Cube Group TheoryTraveling SalesmanVoronoi and DelaunayFractalColumn ArithmeticDraw Math FiguresArithmetic AnimationArithmetic Word ProblemsCounting with Tree DiagramsCube NetsCross SectionsMotion PathMechanicsWavesElectromagnetic WavesLight and LensesThermodynamicsHow Semiconductors WorkMolecular StructuresAtomic OrbitalsElectrochemical CellsChemical EquilibriumCrystal LatticesBuffer pHPeriodic TableComplex IonsDNA Double HelixCell DivisionMembrane ChannelsNerve ImpulseMuscle ContractionRock ClassificationWeatherConstellationsSolar and Lunar Eclipses3D ModelingFloor PlanSeismic StructuresIntersection TurnMaglevCooking AnimationOrigamiLive Viewer CountGeoJSON MapRailway MapPopulation MapCrime MapLand Price MapSchool MapShrine and Castle MapHouse of Representatives MapWord MapSolitaireReversiHakoiri MusumeChessHamburgerRippleSlide Puzzle MakerNeon PinballNovel MakerJapanese Typing PracticePiano Score EditorMusic TheoryShogi StrategyPiano Rhythm Game

English

Python で __enter__ と __exit__ を実装する

自分でコンテキストマネージャを作るには、クラスに __enter__()__exit__() メソッドを実装します。これにより、with 文で使えるオブジェクトを定義できます。

基本的な構造

コンテキストマネージャには2つの特殊メソッドが必要です。

__enter__(self)

with ブロックに入る際に呼ばれる。as で受け取る値を返す。

__exit__(self, exc_type, exc_val, exc_tb)

with ブロックを抜ける際に呼ばれる。例外情報を受け取る。

シンプルな例

処理の開始と終了をログ出力するコンテキストマネージャを作ってみましょう。

class Logger:
    def __init__(self, name):
        self.name = name
    
    def __enter__(self):
        print(f"[{self.name}] 開始")
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print(f"[{self.name}] 終了")

with Logger("処理A"):
    print("何かの処理")

# 出力:
# [処理A] 開始
# 何かの処理
# [処理A] 終了

enter の戻り値

__enter__() の戻り値は as の変数に代入されます。

class Connection:
    def __enter__(self):
        print("接続を確立")
        return self  # self を返すのが一般的
    
    def __exit__(self, *args):
        print("接続を切断")
    
    def query(self, sql):
        print(f"実行: {sql}")

with Connection() as conn:
    conn.query("SELECT * FROM users")

# 出力:
# 接続を確立
# 実行: SELECT * FROM users
# 接続を切断

戻り値は self でなくても構いません。

class FileOpener:
    def __init__(self, filename):
        self.filename = filename
        self.file = None
    
    def __enter__(self):
        self.file = open(self.filename, "r")
        return self.file  # ファイルオブジェクトを返す
    
    def __exit__(self, *args):
        self.file.close()

with FileOpener("data.txt") as f:
    print(f.read())

exit の引数

__exit__() は3つの引数で例外情報を受け取ります。

class DebugContext:
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is not None:
            print(f"例外の型: {exc_type}")
            print(f"例外の値: {exc_val}")
            print(f"トレースバック: {exc_tb}")
        return False  # 例外を再送出

with DebugContext():
    raise ValueError("テストエラー")

例外が発生しなかった場合、3つの引数はすべて None になります。

リソース管理の例

ファイルロックを管理するコンテキストマネージャの例です。

import os

class FileLock:
    def __init__(self, filename):
        self.lock_file = filename + ".lock"
    
    def __enter__(self):
        if os.path.exists(self.lock_file):
            raise RuntimeError("ファイルはロック中です")
        with open(self.lock_file, "w") as f:
            f.write("locked")
        return self
    
    def __exit__(self, *args):
        if os.path.exists(self.lock_file):
            os.remove(self.lock_file)

with FileLock("data.txt"):
    print("ロックを取得して処理中")
# ブロック終了でロック解放

クラスベースのコンテキストマネージャは、状態を持つ複雑なリソース管理に適しています。