2.24. Bağlam yöneticileri

with ifadesi önce kurulum kodunu, sonra bir gövdeyi, ardından da yıkım kodunu çalıştırır – gövde yarıda başarısız olsa bile yıkımın çalışacağı garantisiyle. Kurulum ve yıkımı sağlayan yöntem çifti bağlam yöneticisi olarak adlandırılır.

Biçimi şöyledir:

with <expression> as <name>:
    <body>

İfade bir bağlam yöneticisi döndürür. Python onun __enter__ yöntemini çağırır, isteğe bağlı olarak sonucu <name> öğesine bağlar, gövdeyi çalıştırır ve ardından – gövde normal olarak tamamlansa da bir istisna oluştursa da – __exit__ yöntemini çağırır.

Akış şeması: önce __enter__ çalışır, sonra gövde; gövde ya normal olarak tamamlanır ya da bir istisna oluşturur; her iki durumda da, sonunda __exit__ çalışır.

__exit__, blok içinde ne olursa olsun bloğun sonunda çalışır.

2.24.1. Bir bağlam yöneticisi kullanmak

Tipik örnek, bir dosya açmaktır:

with open("data.txt") as f:
    text = f.read()

# f is now closed, even if read() failed

open(), kendisi bir bağlam yöneticisi olan bir dosya nesnesi döndürür; __enter__ dosyayı döndürür ve __exit__ onu kapatır. with bloğu, “işin bitince dosyayı her zaman kapat” davranışını, çağıranın hatırlaması gereken bir şey olmaktan çıkarıp varsayılan hale getirir.

2.24.1.1. Birden fazla bağlam yöneticisi

Tek bir with ifadesi, virgüllerle ayrılmış olarak aynı anda birkaç bağlam yöneticisine girebilir:

with open("input.txt") as src, open("output.txt", "w") as dst:
    dst.write(src.read())

İç içe iki with bloğuna eşdeğerdir ancak daha düzdür. Yöneticiler soldan sağa girilir ve ters sırayla çıkılır; sağdaki yöneticide __enter__ bir istisna oluşturursa, soldaki yöneticinin __exit__ yöntemi yine de çalışır.

2.24.2. Bir bağlam yöneticisi yazmak

__enter__ ve __exit__ yöntemlerine sahip herhangi bir sınıf bir bağlam yöneticisi olarak çalışır:

class Section:
    def __init__(self, label):
        self.label = label

    def __enter__(self):
        print("---", self.label, "---")
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print("--- end", self.label, "---")
        return False

with Section("setup"):
    print("doing the work")

Çıktı:

--- setup ---
doing the work
--- end setup ---

__exit__, bloğu sonlandıran istisnayı tanımlayan üç argüman ya da blok normal olarak tamamlandıysa üç None değeri alır. False (veya None) döndürmek, yıkımdan sonra herhangi bir istisnanın yayılmasına izin verir; True döndürmek onu yutar.

Yalnızca dosyalar için değil, “aç / kapat” veya “edin / serbest bırak” yaşam döngüsüne sahip her kaynak için bağlam yöneticilerini kullanın. Bu kalıp, temizliği kurulumla, her ikisinin de tanıtıldığı noktada eşleştirir, böylece uzun bir fonksiyonun ortasında unutulan bir kapatma kaynağı sızdıramaz.