Ana içeriğe atla

Python'da Özel Context Manager Yazma

Python'da kaynak yönetiminin ileri düzey yönlerini, özel context manager yazmayı öğrenerek keşfedin.
Güncel 3 Eki 2026

Yapay Zeka ile Keşfedin

ChatGPTClaudePerplexity

Motivasyon

with deyimine, Python'da kaynakları ele almanın özlü bir yoluna, zaten aşina olabilirsiniz. Peki perde arkasında nasıl çalıştığını hiç merak ettiniz mi? with anahtar sözcüğünün gücü context manager'lardan gelir.

Context manager'lar, Python'da kaynak yönetimine yapılandırılmış bir yaklaşım sunan temel bir tasarım kalıbıdır. Kaynakların edinilmesini, doğru şekilde kullanılmasını ve hatalar ya da istisnalar olsa bile en sonunda serbest bırakılmasını veya temizlenmesini garanti ederler.

Bu, dosyalar, ağ bağlantıları veya veritabanı tanıtıcıları gibi kaynaklarla çalışırken özellikle önemlidir. Context manager kullanarak, bir dosyayı kapatmayı ya da bir kilidi serbest bırakmayı unutma endişesinden kurtularak daha temiz ve güvenilir kod yazabiliriz.

Bu eğitimde, Python'un varsayılan olarak sunduğu context manager'ların ötesine geçerek kendi context manager'larımızı yazmayı öğreneceğiz.

Python'da Context Manager'ları Anlamak

Perde arkasında, context manager'lar iki özel yöntemi tanımlayan nesnelerdir: __enter__ ve __exit__. __enter__ yöntemi, with bloğuna girdiğinizde çağrılır ve dönüş değeri bu blok içindeki bir değişkene atanır. Öte yandan __exit__ yöntemi, with bloğu normalde ya da bir istisnayla sona erse de çağrılır.

Bu yapı, doğru kaynak yönetimini sağlar. Bunu daha iyi anlamak için Python'daki bazı yerleşik context manager'lara bakalım:

1. Dosya Yönetimi

Klasik bir dosya açma örneğini ele alalım:

with open('file.txt', 'r') as file:
   data = file.read()

Burada, open('file.txt', 'r') bir context manager olarak davranır. with bloğuna girdiğinizde, dosya nesnesinin enter yöntemi çağrılır, dosya açılır ve file değişkenine atanır. Ardından dosya içeriğine erişmek için file.read() kullanabilirsiniz.

Sonrasında, dosya nesnesinin __exit__ yöntemi, with bloğu çıkarken bir istisna oluşsa bile mutlaka çağrılır. Bu yöntem, dosyayı kapatmakla ilgilenir; böylece açık dosya tanıtıcıları bırakmamış olursunuz.

2. İş Parçacığı Kilitleme

Dosyaların ötesinde, context manager'lar threading.Lock() kullanılarak iş parçacığı eşzamanlaması için de kullanılabilir:

import threading

lock = threading.Lock()
with lock:
   # Critical section
   print("This code is executed under lock protection.")

Burada, lock bir threading.Lock nesnesi, yani başka bir context manager'dır. with bloğuna girdiğinizde __enter__ yöntemi kilidi edinir, böylece aynı anda yalnızca bir iş parçacığı kritik bölümü çalıştırabilir. Son olarak, with bloğundan çıkarken __exit__ yöntemi kilidi serbest bırakır ve diğer iş parçacıklarının devam etmesine izin verir.

3. Veritabanı Bağlantıları

Benzer şekilde, context manager'lar veritabanı bağlantılarını da yönetebilir:

import sqlite3

with sqlite3.connect('database.db') as connection:
    cursor = connection.cursor()
    cursor.execute("SELECT * FROM table")
    rows = cursor.fetchall()

sqlite3.connect('database.db') çağrısı bir context manager'dır. with bloğuna girildiğinde veritabanına bir bağlantı kurulur ve connection değişkenine atanır. Ardından veritabanıyla etkileşim kurmak için bir cursor kullanabilirsiniz. with bloğundan çıkarken __exit__ yöntemi bağlantının kapatılmasını garanti eder ve kaynak sızıntılarını önler.

4. Ağ Soketleri

Context manager'lar ağ iletişimini bile ele alabilir:

import socket

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect(('localhost', 8080))
    s.sendall(b'Hello, world')
    data = s.recv(1024)

Burada, socket.socket(socket.AF_INET, socket.SOCK_STREAM) bir context manager gibi davranan bir soket nesnesi oluşturur. __enter__ yöntemi soketi oluşturur; with bloğu içinde bağlanabilir, veri gönderebilir ve alabilirsiniz. __exit__ yöntemi iş bittiğinde soketin düzgün şekilde kapatılmasını sağlar.

5. Dizin Taraması

Bu kez, os.scandir('.') dizin girdileri üzerinde yineleme yapmanın bir yolunu sağlar:

with os.scandir('.') as entries:
   for entry in entries:
       print(entry.name)

Burada os.scandir('.') context manager olarak davranır. __enter__ yöntemi bir dizin taraması açar ve with bloğu içinde girdiler üzerinde yineleme yapabilirsiniz. __exit__ yöntemi çıkışta dizin taramasını temizler.

Gördüğünüz gibi, context manager'ların gücüyle with deyimi, ayırma ve geri alma işlemlerini otomatikleştirerek kaynak yönetimini basitleştirir.

Python'da özel context manager yazmak

Şimdi, kaynak yönetimi üzerinde daha ince ayarlı denetim sağlamak için kendi özel manager'larınızı nasıl yazacağınıza bakalım.

Özel context manager yazmanın iki temel yaklaşımı vardır: sınıf tabanlı ve fonksiyon tabanlı.

Sınıf Tabanlı Yaklaşım

Sınıf tabanlı yaklaşım, context manager yazmanın en yapılandırılmış ve esnek yöntemidir. Burada, __enter__ ve __exit__ özel yöntemlerini uygulayan bir sınıf tanımlarsınız.

Bir çalıştırma süresini ölçen Timer sınıfı örneğine bakalım:

import time


class Timer:
   def __enter__(self):
       self.start_time = time.time()
       return self

   def __exit__(self, exc_type, exc_value, traceback):
       self.end_time = time.time()
       elapsed_time = self.end_time - self.start_time
       print(f"Elapsed time: {elapsed_time} seconds")


# Example usage
if __name__ == "__main__":
   with Timer() as timer:
       # Code block to measure the execution time
       time.sleep(2)  # Simulate some time-consuming operation
Elapsed time: 2.002082347869873 seconds

Burada, Timer sınıfı, with bloğuna girildiğinde başlangıç zamanını yakalamak için __enter__ yöntemini tanımlar. Blok içinde nesneye erişim sağlamak üzere self döndürülür. __exit__ yöntemi, with bloğundan çıkarken geçen süreyi hesaplar ve yazdırır.

Fonksiyon Tabanlı Yaklaşım

Daha kısa ama esneklikten ödün verebilecek bir yaklaşımı tercih ediyorsanız, fonksiyon tabanlı yöntem iyi bir seçenek olabilir. Burada, herhangi bir fonksiyonu bir context manager'a dönüştürmek için contextlib modülündeki contextmanager dekoratörünü kullanırsınız.

Bunun nasıl çalıştığına bakalım:

import time
from contextlib import contextmanager


@contextmanager
def timer():
   start_time = time.time()
   yield
   end_time = time.time()
   elapsed_time = end_time - start_time
   print(f"Elapsed time: {elapsed_time} seconds")


# Example usage
if __name__ == "__main__":
   with timer():
       time.sleep(2)
Elapsed time: 2.0020740032196045 seconds

@contextmanager dekoratörü, timer fonksiyonunu bir context manager'a dönüştürür. Fonksiyon içinde start_time yakalanır ve yield ifadesi yürütmeyi durdurur; böylece with bloğu içindeki kod çalışır.

Son olarak, bitiş zamanı yakalanıp geçen süre yazdırılarak __exit__ işlevselliği sağlanır.

Özetle, __enter__ mantığını yield'dan önce, __exit__ mantığını ise sonrasına yazarsınız. Her iki yaklaşım da aynı sonucu elde eder; seçim yapınız yapı ve okunabilirlik tercihlerinize bağlıdır.

Context Manager'ların Pratik Örnekleri

Artık Python'da özel context manager yazmayı anladığınıza göre, son derece faydalı olabilecek bazı pratik örneklere bakalım.

Dosya İşlemlerini Yönetmek

Dosya işlemleri birçok uygulamada yaygındır. Dosyalardan okuma ya da dosyalara yazma fark etmeksizin, doğru yönetim veri bütünlüğünü ve kaynak verimliliğini sağlar. Özel bir context manager kullanarak bir dosyaya metin yazmak istediğimiz bir senaryoyu düşünelim.

class FileManager:
   def __init__(self, filename, mode):
       self.filename = filename
       self.mode = mode
       self.file = None

   def __enter__(self):
       self.file = open(self.filename, self.mode)
       return self.file

   def __exit__(self, exc_type, exc_value, traceback):
       if self.file:
           self.file.close()


# Example usage
if __name__ == "__main__":
   with FileManager("example.txt", "w") as file:
       file.write("Hello, world!\n")

Bu örnekte, dosya işlemlerini ele alan Python'daki open işlevini taklit etmek için bir FileManager sınıfı tanımlıyoruz. Her yöntemin ne yaptığını parçalayalım:

  • __init__(self, filename, mode): Bu yöntem sınıfın kurucusudur. Açılacak dosyayı ve hangi kipte açılacağını (ör. okuma, yazma, birleştirme) belirten filename ve mode ile FileManager nesnesini başlatır.
  • __enter__(self): Her zamanki gibi, with bloğuna girildiğinde çağrılır. filename ile belirtilen dosyayı, mode ile belirtilen kipte açar. Açılan dosya nesnesi self.file'a atanır ve with bloğu içinde kullanılmak üzere döndürülür.
  • __exit__(self, exc_type, exc_value, traceback): Blok içinde bir istisna oluşup oluşmadığına bakılmaksızın, with bloğundan çıkarken çağrılır. self.file None değilse self.file.close() çağrılarak dosyanın düzgün şekilde kapatılmasını sağlar. Bu, kaynak sızıntılarını engeller ve doğru temizliği garanti eder.

Yukarıdaki kodu çalıştırdıktan sonra example.txt içeriğini incelerseniz, yazılan metni görmelisiniz:

$ cat example.txt
Hello, world!

Elbette, FileManager sınıfının avantajı, dosyalarla çalışmak için istediğiniz özel mantığı uygulayabilmenizdir. Örneğin, açılan veya kapatılan dosyaların bir metin dosyasına kaydını tutacak şekilde sınıfı değiştirebilirsiniz.

import datetime


class FileManager:
   def __init__(self, filename, mode, log_filename="file_log.txt"):
       self.filename = filename
       self.mode = mode
       self.log_filename = log_filename
       self.file = None

   def log_action(self, action):
       timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
       with open(self.log_filename, "a") as log_file:
           log_file.write(f"{timestamp} - {action}: {self.filename}\n")

   def __enter__(self):
       self.file = open(self.filename, self.mode)
       self.log_action("Opened")
       return self.file

   def __exit__(self, exc_type, exc_value, traceback):
       if self.file:
           self.file.close()
           self.log_action("Closed")


# Example Usage
with FileManager("example.txt", "r") as file:
   content = file.read()
   print(content)

Hello, world!

Bu değiştirilmiş sürümde:

  • Zaman damgalarıyla çalışmak için datetime modülü içe aktarılır.
  • Dosya işlemlerini günlüğe kaydetmek için log_action yöntemi eklenir.
  • Günlük dosyasının adını belirlemek için log_filename parametresi eklenir. Varsayılan değer 'file_log.txt' olarak ayarlanır.
  • __enter__ ve __exit__ yöntemleri içinde log_action kullanılarak dosya adı ve eylem (açıldı ya da kapatıldı) günlük dosyasına eklenir.
  • log_action içinde, geçerli zaman damgasını "YYYY-MM-DD HH:MM:SS" biçiminde almak için datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") kullanılır.
  • Zaman damgası, dosya adı ve eylemle birlikte günlüğe eklenir.

Şimdi, file_log.txt içeriğini yazdırırsak, günlükleri görmeliyiz:

$ cat file_log.txt
2024-04-03 12:37:02 - Opened: example.txt
2024-04-03 12:37:02 - Closed: example.txt

Özel bir context manager olarak uygulayabileceğimiz bir diğer pratik kullanım durumu da bir SQLite veritabanı yönetim aracıdır:

import sqlite3


class DatabaseConnection:
   def __init__(self, database_name):
       self.database_name = database_name
       self.connection = None

   def __enter__(self):
       self.connection = sqlite3.connect(self.database_name)
       return self.connection

   def __exit__(self, exc_type, exc_value, traceback):
       if self.connection:
           self.connection.close()

Bu yine sqlite3.connect işlevini taklit eder, ancak özel veritabanı yönetim mantığı eklenerek daha da iyileştirilebilir. Yöneticinin çalışıp çalışmadığını bazı örnek işlevlerle kontrol edelim:

# Example usage
def create_table():
   with DatabaseConnection("example.db") as connection:
       cursor = connection.cursor()
       cursor.execute(
           """CREATE TABLE IF NOT EXISTS users (
                           id INTEGER PRIMARY KEY,
                           username TEXT,
                           email TEXT)"""
       )


def insert_data(username, email):
   with DatabaseConnection("example.db") as connection:
       cursor = connection.cursor()
       cursor.execute(
           "INSERT INTO users (username, email) VALUES (?, ?)", (username, email)
       )
       connection.commit()


def fetch_data():
   with DatabaseConnection("example.db") as connection:
       cursor = connection.cursor()
       cursor.execute("SELECT * FROM users")
       return cursor.fetchall()


if __name__ == "__main__":
   create_table()
   insert_data("john_doe", "john@example.com")
   insert_data("jane_doe", "jane@example.com")
   users = fetch_data()
   print("Users in the database:")
   for user in users:
       print(user)

Users in the database:
(1, 'john_doe', 'john@example.com')
(2, 'jane_doe', 'jane@example.com')
(3, 'john_doe', 'john@example.com')
(4, 'jane_doe', 'jane@example.com')

Veritabanı işlemlerini işlevlere bölerek ve az önce oluşturduğumuz özel yöneticiyi kullanarak çok daha sürdürülebilir kod yazabiliriz.

Python'da Context Manager'larla İlgili İleri Düzey Konular

Bu bölümde, Python'da context manager'larla ilgili ileri düzey konuları ele alacağız. Bunlar arasında hata yönetimi ve iç içe context manager kullanımı yer alır.

Hata yönetimi

Hata yönetimi programlamanın temel bir yönüdür ve Python'daki context manager'lar, kaynak yönetimi işlemleri içinde hataları yönetmek için kullanışlı bir yol sunar.

FileManager sınıfı örneğinde, bir istisna oluşsa bile doğru temizliği sağlamak için __exit__ yönteminin içinde hata yönetimi uygulanabilir. Örneğin, dosyaya veri yazılırken (yani with bloğu içindeyken) bir hata oluşursa, kaynak sızıntılarını önlemek için dosyanın yine de kapatılmasını isteriz. __exit__ yöntemini şu şekilde değiştirebiliriz:

def __exit__(self, exc_type, exc_value, traceback):
   if self.file:
       self.file.close()
       if exc_type is not None:
           print(f"An error occurred: {exc_value}")

Burada, önce doğru temizliği sağlamak için self.file.close() ile dosyayı kapatıyoruz. Ardından bir istisna olup olmadığını kontrol ediyoruz (exc_type is not None). Eğer varsa, neyin yanlış gittiğine dair geri bildirim sağlamak için bir hata mesajı yazdırıyoruz.

Burada:

  • exc_type, AttributeError ve IndexError gibi istisna sınıfıdır.
  • exc_value, mesajı içeren istisna örneğidir.
  • traceback, hatanın tam iz bilgisidir.

Şimdi, bilerek hatalı bir kodla FileManager sınıfını çalıştıralım:

with FileManager("example.txt", "r") as file:
   # Simulate a sample error
   content = file.read()
   print(content[150])

An error occurred: string index out of range

Dolayısıyla, özel yöneticilerinizde istisnaları ele alırken özel mantık uygulamak için aşağıdaki örnek iş akışını takip edebilirsiniz:

def __exit__(self, exc_type, exc_value, traceback):
   # Perform cleanup
   ...

   # Handle the exception
   if isinstance(exc_value, AttributeError):
       # Handle AttributeError here
       print(f"Exception message: {exc_value}")
   # Handle another exception
   elif isinstance(exc_value, AnotherError):
       ...

Context manager'ları iç içe kullanmak

Context manager'ları iç içe kullanmak, tek bir with deyimi içinde, her biri kendi context manager'ına sahip birden fazla kaynağı aynı anda yönetmenizi sağlar. Bu, birden fazla kaynağı içeren karmaşık işlemlerle uğraşırken özellikle kullanışlıdır. Örneklerimizde context manager'ları nasıl iç içe kullanabileceğimize bakalım.

Örneğin, aynı bağlam içinde bir dosya açmak ve veritabanı işlemleri yapmak isteyebiliriz. Belki de bir CSV dosyasının içeriğini satırlar halinde bir veritabanına aktarmak istiyoruz. Bunu tek bir with deyimi içinde FileManager ve DatabaseConnection context manager'larını iç içe kullanarak başarabiliriz:

if __name__ == "__main__":
   with FileManager("example.txt", "w") as file, DatabaseConnection("example.db") as connection:
       ...

Bu örnekte, FileManager ve DatabaseConnection context manager'ları aynı with deyimi içinde iç içe kullanılır. Bu sayede dosyayı açıp veritabanı bağlantısını aynı anda kurabiliriz. Takip eden dosya yazma ve veritabanı işlemleri bu birleşik bağlam içinde gerçekleştirilir.

Aklınızda bulundurmanız gereken bir nokta kodun okunabilirliğidir. Aynı anda kullanılan bağlam sayısının ikiyi geçmemesini öneririm.

Sonuç

Python'daki context manager'lar, bir with deyimi içinde kaynak yönetimine yapılandırılmış bir yaklaşım sunarak kaynakların doğru şekilde ayrılmasını ve geri alınmasını sağlar.

Bu eğitim boyunca context manager'ların temellerini öğrendik, pratik örneklerden geçtik ve hata yönetimi ile iç içe kullanım gibi ileri konuları tartıştık. Özel context manager yazmayı öğrenerek daha temiz ve güvenilir kod yazabilir, hata yönetimini iyileştirebilir ve kaynakları verimli şekilde yönetebilirsiniz.

Özel context manager yazma konusu “İleri Düzey Python” başlığının altında yer alır. Becerilerinizi daha da geliştirmek için bazı ilgili kaynaklar şunlardır:


Bexruz (Bex) Tuychiev's photo
Author
Bexruz (Bex) Tuychiev
LinkedIn

2 yılı aşkın deneyime sahip bir veri bilimi içerik üreticisiyim ve Medium’da en büyük takipçi kitlelerinden birine sahibim. Yapay zekâ ve makine öğrenimi üzerine, biraz da alaycı bir üslupla yazılmış, ayrıntılı makaleler kaleme almayı seviyorum; çünkü onları biraz daha az sıkıcı hale getirmek için bir şeyler yapmak gerekiyor. 130’un üzerinde makale ve bir de DataCamp kursu ürettim; bir diğeri de hazırlık aşamasında. İçeriklerim 5 milyondan fazla kişi tarafından görüldü; bunların 20 bini Medium ve LinkedIn’de takipçim oldu. 

Konular
Python

Python Yolculuğunuza Bugün Devam Edin!

Program

Python Programlama

19 sa
Programlama becerilerinizi geliştirin. Kodu nasıl optimize edeceğinizi, fonksiyonları ve testleri nasıl yazacağınızı ve en iyi uygulama yazılım mühendisliği tekniklerini nasıl kullanacağınızı öğrenin.
Ayrıntıları GörüntüleRight Arrow
Kursa Başla
Devamını GörRight Arrow
İlgili

blog

2026’da En Popüler 40 Yazılım Mühendisi Mülakat Sorusu

Algoritmalar, sistem tasarımı ve davranışsal senaryoları kapsayan bu temel sorularla teknik mülakat sürecine hakim olun. Uzman cevapları, kod örnekleri ve kanıtlanmış hazırlık stratejileri edinin.
Dario Radečić's photo

Dario Radečić

15 dk.

blog

Hızlı Sevkiyat İçin Pratik Vibe Kodlama Teknoloji Yığını

Ön uç, arka uç, veritabanları, kimlik doğrulama, depolama, e-posta, test, dağıtım ve izleme için en iyi araçları keşfedin.
Abid Ali Awan's photo

Abid Ali Awan

14 dk.

Eğitim

Python'da Listeyi String'e Nasıl Dönüştürürsünüz

Bu hızlı eğitimde, Python'da bir listeyi string'e nasıl dönüştüreceğinizi öğrenin.
Adel Nehme's photo

Adel Nehme

Eğitim

.gitignore Nasıl Kullanılır: Örneklerle Pratik Bir Giriş

Git deponuzu temiz tutmak için .gitignore’u nasıl kullanacağınızı öğrenin. Bu eğitim; temelleri, yaygın kullanım durumlarını ve başlamanıza yardımcı olacak pratik örnekleri kapsar!
Kurtis Pykes 's photo

Kurtis Pykes

8 dk.

Devamını GörDevamını Gör