Kurs
Bağımlılık enjeksiyonu, nesnelerin veya servislerin (burada bağımlılıklar kastedilir) sınıf tarafından içeride oluşturulması yerine dışarıdan sağlandığı bir tasarım desenidir. Bu, bileşenlerin birbirinden ayrılmasını sağlar.
Python'da bağımlılık enjeksiyonu kullanmanın şu gibi birçok faydası vardır:
- Modülerlik: Kodunuz daha küçük, yeniden kullanılabilir parçalara ayrılır.
- Test edilebilirlik: Gerçek parçayı sahte (mock) olanlarla değiştirebildiğiniz için kodunuzu test etmek daha kolaydır.
- Sürdürülebilirlik: Kodunuzun bazı bölümlerini sistemin geri kalanını bozmadan güncelleyebilir veya değiştirebilirsiniz.
Bu eğitimde, basit ve pratik örneklerle Python'da bağımlılık enjeksiyonunun nasıl kullanılacağını göstereceğim.
Python'da Bağımlılık Enjeksiyonunu Anlamak
Bağımlılık enjeksiyonunun nasıl uygulanacağını anlamak için onu yöneten temel ilkeleri kavramanız gerekir.
Bağımlılık enjeksiyonu, Kontrolün Tersine Çevrilmesi (IoC) denilen prensibe dayanır; bu da bir sınıfın bağımlılıklarını kendisinin oluşturup yönetmesi yerine, bunları harici bir kaynaktan alması anlamına gelir. Bu, tek bir göreve odaklanmaya yardımcı olur ve kodunuzu temiz tutar.
Python'da bağımlılık enjeksiyonu en yaygın olarak şu yollarla yapılır; başkaları da vardır:
- Kurucu enjeksiyonu: Bağımlılıkları sınıfın kurucusundan geçirin.
- Setter enjeksiyonu: Nesne oluşturulduktan sonra bir setter yöntemiyle bağımlılıkları ayarlayın.
- Metot enjeksiyonu: Bağımlılıkları doğrudan ihtiyaç duyan metoda geçirin.
Python'da Neden Bağımlılık Enjeksiyonu Kullanılır?
Bir geliştirici olarak, bağımlılık enjeksiyonunu anlamak ve uygulamak, kod tasarımınızda büyük fark yaratabilir. Bağımlılık enjeksiyonunun bazı faydalarına bakalım.
Modülerlik, yeniden kullanılabilirlik ve esneklik
Bağımlılık enjeksiyonu, kodunuzu daha küçük ve odaklı bileşenlere ayırmanıza olanak tanır; böylece daha fazla esneklik ve modülerlik sağlar.
Her parça veya bileşen belirli bir görevi üstlenir ve harici bağımlılıklara dayanır; bu da onu sizin ya da başkasının uygulamasının farklı bölümlerinde yeniden kullanmayı kolaylaştırır
Örneğin, mesaj gönderen bir sınıfınız olduğunu düşünün. E-posta veya SMS mantığını doğrudan servisinizin içine inşa etmek yerine, bir e-posta ya da SMS gönderici sınıfını enjekte edebilirsiniz.
Artık mesaj gönderen sınıfınız, mesajın nasıl gönderildiğiyle ilgilenmez; yalnızca kendisine sağlanan göndericiyi kullanır. Bu da, mesaj göndericinizi farklı e-posta veya SMS göndericilerle kullanabileceğiniz anlamına gelir.
Sürdürülebilirlik ve test edilebilirlik
Bağımlılık enjeksiyonu kullanırken, test sırasında gerçek bağımlılıkları kolayca sahte verilerle değiştirebilirsiniz. Bu sayede API'lere veya veritabanlarına bağlı kalmadan birim testleri yazmak daha basit hale gelir.
Bir düşünce egzersizi olarak, verileri veritabanına kaydeden bir sınıfınız olduğunu varsayalım. Test amacıyla, testinizi üretim veritabanına bağlamamak için gerçek bir veritabanı yerine sahte bir veritabanı enjekte edebilirsiniz.
Bağımlılık enjeksiyonu, kodunuzun belirli bölümlerini tüm sistemi bozmadan değiştirmenizi sağlar. Bu, özellikle Stripe'tan PayPal'a gibi bir uygulamada ödeme sağlayıcısını değiştirme gereksinimi olduğunda, geri kalan kodu değiştirmeden işinize yarar.
Kontrolün tersine çevrilmesi ve değiştirilebilir bağımlılıklar
Kontrolün tersine çevrilmesi fikri şudur: bağımlılık enjeksiyonu, bağımlılıkların oluşturulması ve yönetilmesi sorumluluğunu harici bir kaynağa kaydırır.
Bu da bir uygulamayı diğerinin yerine kolayca koyabileceğiniz anlamına gelir; örneğin, raporları biçimlendiren ve dışa aktaran bir ReportGenerator sınıfınız varsa.
Sınıfın hangi biçime dışa aktaracağına karar vermesi yerine, içine PDFExporter, ExcelExporter veya CSVExporter gibi bir dışa aktarıcı sınıf enjekte edebilirsiniz.
ReportGenerator sınıfı dışa aktarmanın nasıl gerçekleştiğiyle ilgilenmez; bu yüzden bir biçimi değiştirmek için, enjekte ettiğiniz dışa aktarıcıyı değiştirmeniz yeterlidir; ReportGenerator'ı hiç değiştirmeden.
Python'da Bağımlılık Enjeksiyonunun Uygulanması
Artık bağımlılık enjeksiyonunun ne olduğuna ve neden faydalı olduğuna dair net bir fikir edindiğinize göre, bunu Python'da nasıl uygulayabileceğinize bakalım. En basit yöntemle başlayalım: manuel bağımlılık enjeksiyonu.
Manuel bağımlılık enjeksiyonu
Manuel bağımlılık enjeksiyonunda, bağımlılıkları siz oluşturur ve onlara ihtiyaç duyan sınıf veya fonksiyona geçirirsiniz.
Bu yöntem küçük uygulamalar için iyi çalışır, ancak uygulamanız büyüdükçe hatalardan kaçınmak için bir bağımlılık enjeksiyonu çerçevesi kullanmanız gerekir.
İşte bağımlılık enjeksiyonu olmadan yazılmış bir kod örneği
class EmailService:
def send_email(self, message):
print(f"Sending email: {message}")
class UserNotifier:
def __init__(self):
self.email_service = EmailService() # Creates its own dependency
def notify(self, message):
self.email_service.send_email(message)
notifier = UserNotifier()
notifier.notify("Welcome!")
Yukarıdaki örnekte, UserNotifier sınıfı EmailService'e sıkı sıkıya bağlıdır. Test için EmailService'i kolayca değiştirip sahte bir servis kullanamazsınız.
İşte manuel bağımlılık enjeksiyonu ile yazılmış başka bir sürüm.
class EmailService:
def send_email(self, message):
print(f"Sending email: {message}")
class UserNotifier:
def __init__(self, email_service):
self.email_service = email_service # Dependency is injected
def notify(self, message):
self.email_service.send_email(message)
email_service = EmailService()
notifier = UserNotifier(email_service)
notifier.notify("Welcome!")
Yukarıdaki sürüm esnektir ve çeşitli mesajlaşma servislerini enjekte etmenize olanak tanır; testlerde EmailService'i bir sahte ile de değiştirebilirsiniz.
dependency-injector çerçevesini kullanma
Uygulamanız büyüdükçe, bağımlılıkları yönetmek karmaşık ve dağınık hale gelir.
dependency-injector çerçevesi yapılandırılmış bir yaklaşım sunar.
Konteyner–sağlayıcı mimarisi
dependency-injector çerçevesi, konteyner–sağlayıcı mimarisiyle çalışır.
- Konteyner: Uygulama bağımlılıklarınız için merkezi kayıt defteridir.
- Sağlayıcılar: Bağımlılıkların nasıl oluşturulacağını tanımlar.
- Yapılandırma: Ayarları değiştirmenize ve nesnelere enjekte etmenize olanak tanır.
- Geçersiz kılma: Özellikle test sırasında, uygulama kodunu değiştirmeden bağımlılıkları değiştirmenizi sağlar.
from dependency_injector import containers, providers
# Services
class EmailService:
def send_email(self, message):
print(f"Sending email: {message}")
class UserNotifier:
def __init__(self, email_service):
self.email_service = email_service
def notify(self, message):
self.email_service.send_email(message)
# Container
class Container(containers.DeclarativeContainer):
email_service = providers.Singleton(EmailService)
user_notifier = providers.Factory(UserNotifier, email_service=email_service)
# Usage
container = Container()
notifier = container.user_notifier()
notifier.notify("Hello!")
Yukarıdaki örnekte:
-
Container, örneklerin nasıl oluşturulduğunu tanımlar -
Singleton, yalnızca bir adetEmailServiceörneği olmasını sağlar -
Factory,EmailService'i otomatik olarak enjekte ederek yeni birUserNotifieroluşturur.
Bağlama (wiring) mekanizması
@dekoratörler gibi @inject kullanarak manuel bağımlılık geçirme ihtiyacını azaltabilirsiniz.
Önceki örneğin başka bir sürümü aşağıdadır:
from dependency_injector import containers, providers
# Define services
class EmailService:
def send_email(self, message):
print(f"Sending email: {message}")
class UserNotifier:
def __init__(self, email_service: EmailService):
self.email_service = email_service
def notify(self, message):
self.email_service.send_email(message)
# Create container
class Container(containers.DeclarativeContainer):
email_service = providers.Singleton(EmailService)
user_notifier = providers.Factory(UserNotifier, email_service=email_service)
from dependency_injector.wiring import inject, Provide
@inject
def main(notifier: UserNotifier = Provide[Container.user_notifier]):
notifier.notify("Hello!")
if __name__ == "__main__":
container = Container()
container.wire(modules=[__name__]) # This wires the current module
main()
Bu sürümde;
-
Bağımlılığı elle eklemeye gerek kalmadan,
@injectdekoratörü fonksiyonun veya sınıfın bağımlılığını otomatik olarak enjekte eder. -
Providesınıfı,dependency-injector'a hangi bağımlılığın enjekte edileceğini söyler.
Bu yaklaşım, bağımlılıkların konteynerden manuel bağlanması ihtiyacını ortadan kaldırır.
Birden fazla bileşene bağlı olabilen servislerin bulunduğu daha büyük uygulamalarda (dolayısıyla otomatik başlatma ihtiyacı) faydalıdır.
Popüler Python Çatılarında Bağımlılık Enjeksiyonu
Çoğu modern Python çatısı, daha temiz ve daha test edilebilir kod yazmanıza olanak tanıyan bağımlılık enjeksiyonu ile çalışmayı kolaylaştırır.
Bu bölümde, Flask, Django ve FastAPI'de bağımlılık enjeksiyonunun nasıl uygulandığını öğreneceksiniz.
Flask'te bağımlılık enjeksiyonu
Flask'in yerleşik bağımlılık enjeksiyonu desteği yoktur; ancak aşağıdaki adımlarla dependency-injector kütüphanesini kullanarak bunu uygulayabilirsiniz.
Adım 1: Servislerinizi tanımlayın
class GreetingService:
def get_greeting(self, name):
return f"Hello, {name}!"
Adım 2: Bağımlılık enjeksiyonu konteynerinizi kurun
from dependency_injector import containers, providers
class Container(containers.DeclarativeContainer):
greeting_service = providers.Factory(GreetingService)
Adım 3: Flask uygulamasını oluşturun ve bağımlılıkları enjekte edin.
from flask import Flask, request
from dependency_injector.wiring import inject, Provide
container = Container()
app = Flask(__name__)
@app.route("/greet")
@inject
def greet(greeting_service: GreetingService = Provide[Container.greeting_service]):
name = request.args.get("name", "World")
return greeting_service.get_greeting(name)
container.wire(modules=[__name__])
if __name__ == "__main__":
app.run(debug=True)
Yukarıdaki koda göre:
-
name = request.args.get(“name”, “World”)fonksiyonu, sorgu dizgisindenname'i alır ve sağlanmadıysa varsayılan olarak”World”değerini kullanır. -
return greeting_service.get_greeting(name), enjekte edilen servisteki metodu çağırır ve bir selamlama mesajı döndürür. -
container.wire(modules=[__name__]), bağımlılık enjektörüne bu modülü@injectdekoratörleri veProvide[...]işaretleyicileri için taramasını ve bunları gerçek sağlayıcılara bağlamasını söyler.
Adım 4: Uygulamayı test edin
Uygulamayı terminalinizde python <name_of_file>.py komutuyla çalıştırın ve http://localhost:5000/greet?name=Jacob URL'sini ziyaret edin.
Şunu görmelisiniz.

Django'da bağımlılık enjeksiyonu
Tıpkı Flask gibi, Django da yerleşik bağımlılık enjeksiyonu sunmaz. Ancak, sınıf tabanlı görünümlerini, ara katman yazılımlarını ve uygulama yapısını kullanarak bağımlılık enjeksiyonunu manuel olarak veya dependency-injector kütüphanesi ile entegre edebilirsiniz.
İşte Django'da bağımlılık enjeksiyonunu uygulamak için adım adım bir prosedür.
Adım 1: Servisinizi tanımlayın
Uygulama klasörünüz altında, iş mantığınızı içeren basit bir servisi services.py adlı yeni bir dosyada oluşturun.
# myapp/services.py
class GreetingService:
def get_greeting(self, name):
return f"Hello, {name}!"
Adım 2: Bağımlılık enjektörü konteynerini kurun
Uygulama dizininizde, dependency-injector kütüphanesini kullanarak containers.py adlı yeni bir dosyada bir konteyner oluşturun.
# myapp/containers.py
from dependency_injector import containers, providers
from .services import GreetingService
class Container(containers.DeclarativeContainer):
greeting_service = providers.Factory(GreetingService)
Adım 3: Enjekte edilmiş bağımlılıklarla bir View oluşturun
views.py içinde bağımlılıkları almak için @inject dekoratörünü kullanın.
# myapp/views.py
from django.http import HttpResponse
from dependency_injector.wiring import inject, Provide
from .containers import Container
from .services import GreetingService
@inject
def greet_view(
request, greeting_service: GreetingService = Provide[Container.greeting_service]
):
name = request.GET.get("name", "World")
message = greeting_service.get_greeting(name)
return HttpResponse(message)
Adım 4: urls.py'ı güncelleyin
Proje klasörünüz altında, urls.py dosyasına gidin ve bağımlılık enjektörünüzü Django'nun URL yönlendirme sistemine bağlayın.
from django.contrib import admin
from django.urls import path
from myapp.views import greet_view
urlpatterns = [
path("admin/", admin.site.urls),
path("greet/", greet_view),
]
Adım 5: apps.py içinde konteyneri bağlayın
apps.py'a gidin ve konteyneri uygulamaya bağlayın.
# myapp/apps.py
from django.apps import AppConfig
from .containers import Container
class MyAppConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "myapp"
def ready(self):
container = Container()
container.wire(modules=["myapp.views"])
Ardından INSTALLED_APPS altındaki uygulama yapılandırmanıza gidin ve uygulamanızı ekleyin.
# settings.py
INSTALLED_APPS = [
'myapp.apps.MyAppConfig',
...
]
Adım 6: Görünümü test edin
Django sunucunuzu başlatın ve görünümü http://localhost:8000/greet/?name=Django URL'si üzerinden açın.
Şunu görmelisiniz.

FastAPI'de Bağımlılık Enjeksiyonu
FastAPI, bir fonksiyonun döndürdüğünü enjekte eden özel Depends() işaretleyicisi aracılığıyla yerleşik bağımlılık enjeksiyonu desteğine sahiptir.
İşte FastAPI bağımlılık enjeksiyonu için adım adım bir rehber.
Adım 1: Bir servis tanımlayın
class GreetingService:
def get_greeting(self, name: str) -> str:
return f"Hello, {name}!"
Adım 2: Bağımlılık fonksiyonunu oluşturun
def get_greeting_service():
return GreetingService()
Adım 3: FastAPI uygulamasını kurun ve Bağımlılık Enjeksiyonu kullanın
app = FastAPI()
@app.get("/greet")
def greet(
name: str = "World", service: GreetingService = Depends(get_greeting_service)
):
return {"message": service.get_greeting(name)}
Adım 4: Uygulamayı çalıştırın ve test edin
Sunucunuzu uvicorn main:app –reload komutuyla başlatın ve http://localhost:8000/greet?name=FastAPI URL'sini ziyaret edin. Aşağıdaki yanıtı görmelisiniz.

Python Bağımlılık Enjektörü Çerçevelerinin Karşılaştırılması
Farklı güçleri, sözdizimleri ve kullanım alanları olan çeşitli Python bağımlılık çerçeveleri vardır. Doğru çerçeveyi seçmek, projenizin boyutuna ve yapısına bağlıdır.
İşte bazı yaygın Python bağımlılık enjeksiyonu çerçevelerinin bir karşılaştırması.
|
Çerçeve |
En Uygun Alan |
Güçlü Yanlar |
Sınırlamalar |
|
|
Üretimde kullanılan karmaşık uygulamalar |
Tam özellikli, hızlı ve yapılandırılabilir |
Detaycı/uzun sözlü |
|
Basit ve minimum uygulamalar için uygun |
Temiz sözdizimi |
Diğerlerine göre daha az gelişmiş. |
|
|
Gelişmiş bağlama mantığı |
Otomatik bağlama (auto-wiring) |
Yansıtma (introspection) nedeniyle daha yavaş |
|
|
Basit uygulamalar |
Minimal ve hızlı |
Diğerlerine kıyasla sınırlı özellikler |
|
|
FastAPI uygulamaları |
Async için yerleşik destek, ayrıca test edilebilir |
FastAPI dışında yeniden kullanılamaz |
|
|
Flask uygulamaları |
Flask uygulamalarıyla kolay entegrasyon |
|
|
|
Django projeleri |
Görünümler ve ara katmanlar için bağımlılık enjeksiyonu sağlar |
|
İleri Düzey Uygulama Kalıpları
Uygulama kod tabanınız büyüdükçe, bağımlılık enjeksiyonunu sürdürmek zorlaşır.
İstek kapsamlı kaynaklar, temizleme (cleanup) ve sorumluluk ayrımı gibi görevleri ele almak için ileri seviye bağımlılık enjeksiyonunu kullanabilirsiniz.
Kapsamlı bağımlılıklar
Kapsamlı bağımlılıklar, bir bağımlılığın ne kadar süreyle kalacağını kontrol etmenizi sağlar; örneğin, istek başına yeni bir nesne, uygulamanın ömrü boyunca paylaşılan bir nesne veya veritabanı bağlantıları gibi kaynakların otomatik temizliği.
Çoğu bağımlılık enjektörü çerçevesi şu kapsamları destekler:
- Singleton: Tüm uygulama için tek bir örnek.
- Factory: Her enjeksiyonda yeni bir örnek.
- İş parçacığı yereli veya istek: İş parçacığı/istek başına tek bir örnek.
- Kaynak: Kurulum ve yıkım (teardown) mantığıyla yönetilir.
Aşağıda, dependency-injector kullanarak iç içe konteynerlerle kapsamlı bağımlılıklara bir örnek verilmiştir.
from dependency_injector import containers, providers, resources
# A simple resource that needs setup and cleanup
class MyResource:
def __enter__(self):
print("Setting up the resource")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("Cleaning up the resource")
# Create a container to manage the resource
class MyContainer(containers.DeclarativeContainer):
resource = providers.Resource(MyResource)
# Using the resource with a context manager
container = MyContainer()
with container.resource() as r:
print("Using the resource")
Yukarıdaki örnekte, MyResource bir dosyayı/veritabanını açıp kapatma gibi kurulum ve yıkım gerektiren bir sınıfı taklit eder.
container, providers.Resource kullanarak kaynağı sağlar ve with container.resource() kullandığınızda, bağlama doğru şekilde girilip çıkılır.
Bu kalıp, dosya tanıtıcıları, veritabanı bağlantıları veya ağ oturumları gibi kaynakların temiz bir şekilde yönetilmesini sağlar.
Geçerli istek için bir veritabanı oturumuna veya istek özelinde bağlamı olan bir kaydediciye (logger) ihtiyaç duyan bir API uç noktası hayal edin.
Kapsamlı bağımlılıklarla, çatı istek başladığında istek kapsamlı bir konteyner oluşturur ve tüm bağımlılıkları bu kapsamdan çözümler.
İstek bittiğinde, veritabanı oturumlarının kapatılması gibi gerekli tüm temizlik işlemleri otomatik olarak gerçekleşir. Bu, istekler arasında durum paylaşımını önler ve kaynak sızıntılarından kaçınır.
Asenkron enjeksiyon
G/Ç bağlı görevleri verimli şekilde ele almak için asenkron yürütmeye ihtiyaç vardır. FastAPI ve dependency-injector gibi çerçevelerdeki bağımlılık enjeksiyonu sistemleri, async/await kullanarak asenkron enjeksiyonu destekler.
Asenkron enjeksiyon, async def fonksiyonları olan bağımlılıkların enjekte edilmesi veya asenkron veritabanı bağlantıları, harici API'ler ya da arka plan görevleri gibi async resources'ların yönetilmesi anlamına gelir.
Bunları eşzamanlı çözmek yerine, bağımlılık enjeksiyonu sistemi enjekte etmeden önce çözümlenmelerini bekler.
Aşağıda FastAPI'de bir asenkron bağımlılığa örnek verilmiştir:
from fastapi import FastAPI, Depends
app = FastAPI()
# Async dependency
async def get_user():
# Simulate async I/O
await some_async_db_query()
return {"name": "Philip Jones"}
@app.get("/profile")
async def read_profile(user: dict = Depends(get_user)):
return {"user": user}
FastAPI, get_user'ın asenkron çözümlemesini otomatik olarak ele alır ve sonucu enjekte eder.
dependency-injector da AsyncResource sağlayıcılarını kullanarak asenkron yaşam döngüsü yönetimini destekler.
from dependency_injector import containers, providers, resources
class AsyncDB:
async def connect(self):
print("Connected")
return self
async def disconnect(self):
print("Disconnected")
class AsyncDBResource(resources.AsyncResource):
async def init(self) -> AsyncDB:
db = AsyncDB()
return await db.connect()
async def shutdown(self, db: AsyncDB):
await db.disconnect()
class Container(containers.DeclarativeContainer):
db = providers.Resource(AsyncDBResource)
# Usage
async def main():
container = Container()
await container.init_resources()
db = await container.db() # Get the AsyncDB instance
# Use db here...
await container.shutdown_resources()
Yukarıdaki kodda:
-
resources.AsyncResource, async kurulum/yıkım mantığı için kullanılır. -
await container.init_resources(), tüm kaynakları başlatır veinit'i çağırır. -
await container.shutdown_resources(), tüm kaynakları temizler. -
await container.db(), gerçek kaynak örneğini, yaniAsyncDB'yi alır.
Asenkron bağımlılık enjeksiyonu kullanmak, asenkron rotalarda eşzamanlı G/Ç'den kaçınarak uygulamanızın performansını yüksek tutmaya yardımcı olur ve mantığınızda bağımlılıkları elle await etmeye gerek bırakmaz.
Güvenlik Etkileri
Bağımlılık enjeksiyonu modülerliği ve test edilebilirliği artırsa da, dikkatli yönetilmezse potansiyel güvenlik riskleri doğurabilir.
Önemli bir risk, kötü niyetli paketlerin meşru olanları taklit ettiği bağımlılık karışıklığıdır. Örneğin, saldırganlar, PyPI'ye, kurumsal iç sistemlerde kullanılan adlarla paketler yükleyerek uygulamaları kötü amaçlı sürümü kullanmaları için kandırmışlardır.
Güvende kalmak için, asla güvenilmeyen veya kullanıcı kontrolündeki bileşenleri enjekte etmeyin ve ortam değişkenleri gibi yapılandırma kaynaklarını enjekte edilmeden önce doğrulayın.
requirements.txt veya poetry.lock dosyalarını kullanarak sürümleri sabitleyin ve bağımlılık değişikliklerinde güvenlik taramalarını otomatikleştirin. Ayrıca bağımlılıklarınızı düzenli olarak zafiyetler için denetlediğinizden emin olun.
Bağımlılıklarınızın kontrolünü merkezîleştirmek de önemlidir; bu, uygulamalarınız arasında tutarlılık sağlar ve güvenlik yamalarının veya sürüm güncellemelerinin daha kolay uygulanmasını mümkün kılar. Kod tabanının farklı yerlerinde farklı şekillerde enjekte edilen "gölge bağımlılıkları"nı önlemeye yardımcı olur.
Birkaç araç, riskleri tespit edebilir ve güvenlik duruşunuzu iyileştirebilir:
- Snyk: Python paketlerindeki bilinen zafiyetleri tarar ve düzeltmeler önerir.
- Xygeni: CI/CD hatları için koruma sunar.
- Safety: Güvensiz paketleri ve kod zafiyetlerini kontrol eder.
- Bandit: Güvenlik odaklı bir Python statik kod analizörü.
Gerçek Dünya Uygulamaları
Bağımlılık enjeksiyonu yalnızca teorik bir desen değildir; üretim sistemlerinde karmaşıklığı yönetmek, esnekliği artırmak ve testleri kolaylaştırmak için yaygın olarak kullanılır.
Web servisi yapılandırması
Modern web uygulamalarında, kimlik doğrulama, günlükleme (logging) ve veritabanı erişimi gibi standart servislerin yönetiminde bağımlılık enjeksiyonu kritik bir rol oynar.
FastAPI gibi çerçeveler, rotaların ve bağımlılıkların çözümlemesinde önemli bir rol oynar.
Aşağıdaki örnek, FastAPI'de merkezîleştirilmiş bir kimlik doğrulama uygulamasını göstermektedir.
from fastapi import Depends, HTTPException
def get_current_user(token: str = Depends(oauth2_scheme)):
user = verify_token(token)
if not user:
raise HTTPException(status_code=401)
return user
@app.get("/protected")
def protected_route(user: dict = Depends(get_current_user)):
return {"message": f"Hello {user['name']}"}
Yukarıdaki kodda, get_current_user() bir rota bağımlılığı olarak enjekte edilir; ancak belirteci (token) kontrol etme mantığı merkezîdir ve yeniden kullanılabilir.
Aynısını veritabanı erişimi için de yapabilirsiniz; böylece veritabanı oturumları otomatik olarak yönetilir ve temizlenir.
İşte veritabanı erişimini merkezîleştirmek için bağımlılık enjeksiyonunu nasıl kullanabileceğinize bir örnek.
from sqlalchemy.ext.asyncio import AsyncSession
async def get_db_session() -> AsyncSession:
async with async_session() as session:
yield session
@app.get("/items")
async def read_items(db: AsyncSession = Depends(get_db_session)):
return await db.execute("SELECT * FROM items")
Yukarıdaki kodda, get_db_session bir bağımlılık sağlayıcısıdır. read_items rotası çağrıldığında, FastAPI db parametresini otomatik olarak enjekte eder; böylece rota, oturum kurulumunu veya yıkımını elle yönetmeden veritabanına erişebilir.
Test güdümlü geliştirme
Bağımlılık enjeksiyonu, bileşenlerin aldığı bağımlılıkları değiştirmenizi kolaylaştırır; bu da test ederken önemlidir.
Test sırasında gerçek servisleri mocklarla değiştirebilir, bu da kodunuzu daha modüler ve test etmesi kolay hale getirir.
FastAPI ve dependency-injector, test zamanında bağımlılıkların geçersiz kılınmasını destekler.
İşte FastAPI'de bir örnek.
from fastapi.testclient import TestClient
from main import app, get_db_session
def override_db():
return TestDBSession()
app.dependency_overrides[get_db_session] = override_db
client = TestClient(app)
response = client.get("/items")
assert response.status_code == 200
Yukarıdaki kod örneğinde, get_db_session test sırasında sahte bir veritabanıyla geçersiz kılınır; böylece üretim kodunu yamalamaya veya değiştirmeye gerek kalmaz.
Aynı şekilde depedency-injector'da da, test amacıyla gerçek veritabanınızı sahte bir veritabanıyla geçersiz kılabilirsiniz; aşağıdaki örnekte olduğu gibi.
from containers import Container
def test_service_behavior():
container = Container()
container.db.override(providers.Factory(TestDB))
service = container.service()
assert service.get_data() == "mocked"
override() yöntemi, gerçek bağımlılığı sahte bir bağımlılıkla değiştirir; bu temiz, kontrollü ve geri alınabilirdir.
Bağımlılık geçersiz kılmaları için kullanmanız gereken bazı stratejiler şunlardır:
- Kurucu enjeksiyonu: Mock'ları doğrudan kurucuya geçtiğinizden emin olun.
- Çerçeve geçersiz kılmaları: FastAPI'de
FastAPI.dependency_overridesveyadependency-injector'dadependency_injector.override()gibi yerleşik geçersiz kılma araçlarını kullanın. - Fixture'lar:
pytestgibi test çerçevelerini kullanarak yeniden kullanılabilir mock'lar oluşturun ve bunları fixture'lar aracılığıyla enjekte edin.
En İyi Uygulamalar ve Anti-Pattern'ler
Bağımlılık enjeksiyonu kodu modüler ve test etmesi kolay hâle getirmek için tasarlanmış olsa da, hatalara yol açabilir veya kodda karmaşıklık yaratabilir.
Önerilen uygulamalar
Bağımlılıklar oluştururken uymanız gereken bazı en iyi uygulamalar şunlardır:
- Uygulamalara değil, arayüzlere göre programlayın: Bağımlılıkları, karmaşık uygulamalar yerine soyut temel sınıflar veya arayüzler kullanarak tanımlayın. Bu, kodunuzu test etmeyi kolaylaştırır ve birim testlerinde mock kullanımını teşvik eder.
- Miras almaktan ziyade bileşimi tercih edin: Karmaşık sınıf hiyerarşileri inşa etmek yerine, bileşenleri küçük ve odaklı tutmak için enjekte edilmiş bağımlılıklarla nesneleri birleştirin.
- Merkezî bağımlılık yapılandırması: Bağımlılıkları tanımlamak ve yönetmek için tek bir konteyner veya modül kullanın; bu, geçersiz kılmayı ve test etmeyi kolaylaştırır.
- Döngüsel bağımlılıklardan kaçının: Somutlaştırmayı ertelemek için fabrika fonksiyonları veya sağlayıcılar kullanarak bağımlılık grafiğinizi dikkatli tasarlayın.
Yaygın tuzaklar
Bağımlılık enjeksiyonu kullanırken kaçınmanız gereken bazı yaygın tuzaklar şunlardır.
- Servis bulucu anti-pattern'i: Bağımlılıkları dinamik olarak almak için genel bir konteyner kullanmaktan kaçının; bu, bağımlılıkları gizleyebilir ve kodun anlaşılmasını ve test edilmesini zorlaştırabilir.
- Aşırı enjeksiyon: Tek bir sınıfa veya fonksiyona çok fazla servis enjekte etmekten kaçının; bu, şişkin kuruculara ve test etmesi zor mantığa yol açar.
- Kapsam yönetimi hatası ve durum sızıntısı: Kullanıcılar veya testler arasında durum paylaşımını önlemek için her zaman doğru kapsamı kullandığınızdan emin olun.
Python'da Bağımlılık Enjeksiyonu Testi
Bağımlılık enjeksiyonu, birim testlerini basitleştirerek bileşenlerinize sahte nesneler enjekte etmeyi kolaylaştırır. Bu, temel bileşenleri teste özel olanlardan ayırmanızı sağlar.
Bağımlılık enjeksiyonu ile testler belirleyicidir, yan etki içermez ve daha hızlı yürütülür.
İşte testlerinizde bu bağımlılıkların sahte, stub veya fake sürümlerini enjekte etmek için Bağımlılık Enjeksiyonunu nasıl kullanabileceğinizi gösteren bir örnek.
class EmailService:
def send_email(self, to: str, message: str): ...
class Notifier:
def __init__(self, email_service: EmailService):
self.email_service = email_service
def alert(self, user_email):
self.email_service.send_email(user_email, "Alert!")
Testlerde, EmailService'i bir mock ile değiştirin:
class TestEmailService:
def send_email(self, to, message):
self.sent_to = to
def test_notifier_sends_email():
test_email = TestEmailService()
notifier = Notifier(test_email)
notifier.alert("test@example.com")
assert test_email.sent_to == "test@example.com"
Sürdürülebilirlik için bağımlılık enjeksiyonunu sıkça kullanın ve iç isimler değiştiğinde kırılmaları önlemek için monkey patching'den kaçının.
Sonuç
Küçük projeler için başlangıçta bağımlılık enjeksiyonuna ihtiyaç duymayabilirsiniz; ancak proje büyüyüp ölçeklendikçe, bağımlılık enjeksiyonunun sunduğu yapı ve modülerlikten fayda sağlayabilirsiniz.
İleriye baktığımızda, Python'da bağımlılık enjeksiyonunun geleceği umut verici görünmektedir; gelişmiş bir tip sistemi ve geliştirilmiş asenkron yaşam döngüsü yönetimiyle devam etmesini bekleyebiliriz.
Bağımlılık enjeksiyonunu ustalıkla kullanmak, yetkin bir Python geliştiricisi olmanın temelidir. Ancak bununla bitmez; Python becerilerinizi bir üst seviyeye taşıyabilecek birçok ileri konsept daha vardır.
Öğrenme yolculuğunuzu hızlandırmanıza yardımcı olmak için göz atmanız gereken bazı kaynaklarımız şunlardır.
Kurslar:
- Python Paketleri Geliştirme
- PySpark'a Giriş
- Python'da Nesne Yönelimli Programlama
- Python'da Orta Düzey Nesne Yönelimli Programlama
Blog Yazıları:
Python, R ve makine öğrenmesi alanlarında uzmanlığa sahip, deneyimli veri bilimi eğitmeni ve Biyostatistikçi.
SSS
Bağımlılık enjeksiyonu nedir?
Bağımlılık enjeksiyonu, sınıf içinde oluşturulabilecek bağımlılıkların dışarıdan geçilebilmesini sağlayarak daha temiz kod yazmanıza imkân veren bir tasarım desenidir; bu da kodunuzu yönetmeyi ve değiştirmeyi kolaylaştırır.
Bağımlılık enjeksiyonunun başlıca faydaları nelerdir?
Bağımlılık enjeksiyonu kodunuzu modüler, test edilebilir ve sürdürülebilir kılar.
Python'da bağımlılık enjeksiyonunu nasıl uygulayabilirim?
Python'da bağımlılık enjeksiyonunu ya manuel olarak ya da dependency_injector gibi kütüphaneleri kullanarak uygulayabilirsiniz.
Asenkron enjeksiyon nedir?
Asenkron enjeksiyon, async def fonksiyonları olan bağımlılıkların enjekte edilmesi veya asenkron veritabanı bağlantıları, harici API'ler ya da arka plan görevleri gibi async resources'ların yönetilmesi anlamına gelir.
Bağımlılık enjeksiyonunun güvenlik riskleri nelerdir ve nasıl ele alabilirim?
Önemli bir risk, kötü niyetli paketlerin meşru olanları taklit ettiği bağımlılık karışıklığıdır. Bunu önlemek için Snyk, Xygeni, Safety veya Bandit gibi araçları kullanarak kodunuzu ve paketlerinizi zafiyetler açısından tarayabilirsiniz.
