Python'da logging modülü, programınız çalışırken olan olayları önem derecesiyle (DEBUG, INFO, WARNING, ERROR, CRITICAL) kaydetmenizi ve bu kayıtları konsola, dosyaya ya da başka hedeflere istediğiniz biçimde göndermenizi sağlayan standart kütüphane aracıdır. En kısa doğru kullanım, her modülde logger = logging.getLogger(__name__) ile bir logger oluşturmak, programın giriş noktasında bir kez logging.basicConfig(...) çağırmak ve mesajları logger.info(...) gibi yöntemlerle yazmaktır.
Bu yazıda print() ile logging arasındaki farkı, seviyeleri, basicConfig() ayarlarını, logger hiyerarşisini ve yayılmayı (propagation), handler ve formatter nesnelerini, dosyaya ve dönen dosyalara log yazmayı, dictConfig() ile yapılandırmayı, kütüphane yazarları için kuralları ve sık yapılan hataları ele alıyoruz. Örnekler Python 3.15.2 ile çalıştırıldı. Serinin bütünü için Python öğrenme rehberi sayfasına bakabilirsiniz.
print() mi, logging mi?
Resmî Logging HOWTO, hangi durumda hangi aracın kullanılacağını bir tabloyla özetler. Komut satırı programının olağan çıktısı için print() doğru araçtır. Programın normal çalışması sırasındaki olayları (durum izleme, hata araştırma) raporlamak için logger'ın info() yöntemi, çok ayrıntılı tanı bilgisi için debug() kullanılır. Hatayı bildirmek için istisna yükseltilir; hatayı istisna yükseltmeden bastırıp kaydetmek için (örneğin uzun süre çalışan bir sunucu sürecinde) error(), exception() ya da critical() kullanılır.
Uyarılar için ince bir ayrım vardır: sorun kaçınılabilir bir durumsa ve kütüphaneyi kullanan uygulamanın kodunu değiştirmesi gerekiyorsa, kütüphane kodunda warnings.warn() kullanılır; uygulamanın yapabileceği bir şey yoksa ama olayın kaydedilmesi gerekiyorsa logger'ın warning() yöntemi kullanılır. Hata yönetiminin kendisi için Python try except hata yönetimi yazısına bakabilirsiniz.
Seviyeler ve varsayılan davranış

| Seviye | Sayısal değer | Ne zaman kullanılır |
|---|---|---|
| DEBUG | 10 | Ayrıntılı bilgi; genellikle yalnızca sorun ararken ilgilenilir |
| INFO | 20 | Her şeyin beklendiği gibi çalıştığının teyidi |
| WARNING | 30 | Beklenmeyen bir şey oldu ya da yakında sorun çıkabilir; yazılım çalışmaya devam ediyor |
| ERROR | 40 | Daha ciddi bir sorun; yazılım bir işi yapamadı |
| CRITICAL | 50 | Ciddi hata; program çalışmaya devam edemeyebilir |
Varsayılan seviye WARNING'dir: başka bir yapılandırma yapılmadıkça yalnızca bu seviye ve üstündeki olaylar izlenir. HOWTO'daki ilk örnekte de görüldüğü gibi, hiçbir ayar yapmadan logging.warning() çağırırsanız mesaj SEVIYE:logger adı:mesaj biçiminde stderr'e yazılır, logging.info() ise hiçbir şey yazmaz.
import logging
logging.warning('Disk alanı azalıyor')
logging.info('Bu satır görünmez')
# çıktı: WARNING:root:Disk alanı azalıyorModül düzeyindeki logging.warning(), logging.info() gibi fonksiyonlar kök (root) logger üzerinde çalışır ve henüz yapılandırma yapılmamışsa sizin yerinize basicConfig() çağırır. Küçük betikler için bu kullanışlıdır; ama HOWTO, büyük programlarda yapılandırmayı açıkça kontrol etmek ve başka nedenlerle logger oluşturup onun yöntemlerini çağırmanın daha iyi olduğunu belirtir.
Hiç yapılandırma yoksa: "son çare" handler'ı
Kök logger yerine adlandırılmış bir logger kullanır ve hiç yapılandırma yapmazsanız ilginç bir şey olur: mesaj, seviye ya da logger adı olmadan, çıplak olarak görünür. HOWTO'ya göre bu durumda olay lastResort adlı "son çare" handler'ı ile çıkarılır; bu handler stderr'e yazar, biçimlendirme yapmaz ve seviyesi WARNING'dir. Logger'ınızın neden biçimsiz çıktı verdiğini merak ediyorsanız nedeni büyük olasılıkla budur.
import logging
logging.getLogger('kutuphane').warning('yapılandırma yok')
# çıktı: yapılandırma yokbasicConfig() ile hızlı yapılandırma
basicConfig(), kök logger'a bir handler ve formatter ekleyerek temel yapılandırmayı tek çağrıda yapar. En sık kullanılan argümanları level (eşik seviye), format (satır biçimi), datefmt (tarih biçimi), filename ve filemode (dosyaya yazmak için), encoding ve stream'dir (hangi akışa yazılacağı). Aşağıdaki örnekte çıktıyı stdout'a yönlendirdik ve seviyeyi DEBUG yaptık; logger adı olarak __name__ kullandığımız için doğrudan çalıştırılan betikte ad __main__ olarak görünüyor.
import logging
import sys
logging.basicConfig(level=logging.DEBUG, stream=sys.stdout,
format='%(levelname)s | %(name)s | %(message)s')
logger = logging.getLogger(__name__)
logger.debug('ayrıntılı tanı bilgisi')
logger.info('sunucu başlatıldı, port=%d', 8080)
logger.warning('yapılandırma dosyası bulunamadı, varsayılan kullanılıyor')
logger.error('veritabanına bağlanılamadı')
logger.critical('uygulama kapanıyor')
print(logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)
# çıktı: DEBUG | __main__ | ayrıntılı tanı bilgisi
# çıktı: INFO | __main__ | sunucu başlatıldı, port=8080
# çıktı: WARNING | __main__ | yapılandırma dosyası bulunamadı, varsayılan kullanılıyor
# çıktı: ERROR | __main__ | veritabanına bağlanılamadı
# çıktı: CRITICAL | __main__ | uygulama kapanıyor
# çıktı: 10 20 30 40 50Biçim dizesinde en sık kullanılan alanlar %(asctime)s (olayın zamanı), %(levelname)s (seviye adı), %(name)s (logger adı) ve %(message)s'dir (mesajın kendisi). HOWTO'ya göre asctime varsayılan olarak ISO 8601 ya da RFC 3339'a benzer bir biçimde gösterilir ve datefmt argümanı time.strftime() ile aynı biçim kodlarını kabul eder. Tarih biçim kodları için Python datetime yazısına bakabilirsiniz. Diğer alanların tam listesi LogRecord özniteliklerinde yer alır.
basicConfig() hakkında bilinmesi gereken üç kural vardır. Birincisi, belgeye göre kök logger'da zaten handler varsa hiçbir şey yapmaz; ikinci çağrı sessizce etkisiz kalır. Python 3.8'de eklenen force=True argümanı mevcut handler'ları kaldırıp kapatarak yeniden yapılandırmayı sağlar. İkincisi, HOWTO'ya göre logger yöntemleri çağrılmadan önce yapılmalıdır. Üçüncüsü, belge bu fonksiyonun diğer iş parçacıkları başlatılmadan önce ana iş parçacığından çağrılması gerektiğini not eder.
import logging
import sys
logging.basicConfig(level=logging.WARNING, stream=sys.stdout, format='%(levelname)s %(message)s')
logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)
logging.info('görünmez: ikinci basicConfig etkisiz')
logging.basicConfig(level=logging.DEBUG, stream=sys.stdout, format='%(levelname)s %(message)s', force=True)
logging.info('force=True sonrası görünür')
seviye = 'debug'
sayisal = getattr(logging, seviye.upper(), None)
print(sayisal, logging.getLevelName(sayisal))
# çıktı: INFO force=True sonrası görünür
# çıktı: 10 DEBUGÖrneğin son satırları, seviyeyi bir komut satırı argümanından almanın HOWTO'da önerilen yolunu gösteriyor: getattr(logging, seviye.upper(), None) sayısal değeri verir; sonuç int değilse geçersiz bir seviye girilmiştir ve ValueError yükseltmek uygundur. Komut satırı argümanlarını almak için Python argparse yazısına bakabilirsiniz.
Python 3.15: basicConfig(formatter=...)
logging belgesine göre Python 3.15'te basicConfig() bir formatter argümanı kazandı: hazır bir Formatter nesnesini doğrudan verebilirsiniz ve bu nesne oluşturulan bütün handler'lara atanır. formatter ile birlikte format, datefmt ya da style verirseniz bu argümanlar anlamını yitireceği için ValueError yükselir. Örnekte style='{' kullanılıyor; bu ayar yalnızca biçim dizesini etkiler, mesaja eklenen değişkenler yine % tarzıyla birleştirilir.
import logging
import sys
bicim = logging.Formatter('{levelname:<8} {name}: {message}', style='{')
logging.basicConfig(level=logging.INFO, stream=sys.stdout, formatter=bicim)
logging.getLogger('siparis').info('sipariş %s kaydedildi', 1042)
try:
logging.basicConfig(force=True, formatter=bicim, format='%(message)s')
except ValueError as e:
print(type(e).__name__ + ':', e)
# çıktı: INFO siparis: sipariş 1042 kaydedildi
# çıktı: ValueError: 'format' should not be specified together with 'formatter'Değişken veriyi loglamak: % tarzı ve tembel biçimlendirme

Mesaja değişken eklemek için HOWTO, biçim dizesini ve değerleri ayrı argümanlar olarak vermeyi gösterir: logger.warning('%s kullanıcısı %d kez denedi', ad, sayi). Bu eski % biçimlendirme tarzı geriye uyumluluk içindir. Önemli avantajı şudur: belgeye göre mesaj argümanlarının biçimlendirilmesi kaçınılmaz olana kadar ertelenir. Seviye kapalıysa birleştirme hiç yapılmaz. Aşağıdaki örnekte DEBUG mesajı kapalı olduğu için nesnenin __str__ yöntemi hiç çağrılmıyor.
import logging
import sys
class Pahali:
def __str__(self):
print('__str__ çağrıldı')
return 'pahalı değer'
logging.basicConfig(level=logging.INFO, stream=sys.stdout, format='%(levelname)s %(message)s')
log = logging.getLogger('demo')
log.debug('değer: %s', Pahali())
log.info('değer: %s', Pahali())
print(log.isEnabledFor(logging.DEBUG))
# çıktı: __str__ çağrıldı
# çıktı: INFO değer: pahalı değer
# çıktı: FalseDikkat: biçimlendirme ertelenir ama argümanların hesaplanması ertelenmez. Pahali() nesnesi yine de oluşturuldu; yalnızca metne çevrilmedi. Argümanı hesaplamak pahalıysa (örneğin bir veritabanı sorgusu), HOWTO'nun Optimization bölümündeki gibi önce logger.isEnabledFor(logging.DEBUG) ile kontrol edin. f-string kullanırsanız (logger.debug(f'...{x}')) metin, seviye kapalı olsa bile her seferinde oluşturulur; bu yüzden log çağrılarında % tarzı argümanlar tercih edilir. f-string'lerin kendisi Python f-string biçimlendirme yazısında anlatılıyor.
İstisnaları loglamak: logger.exception()
logger.exception() yöntemi ERROR seviyesinde bir mesaj yazar ve mesaja istisna bilgisini (traceback) ekler. Belgeye göre bu yöntem yalnızca bir istisna işleyicisinin, yani except bloğunun içinden çağrılmalıdır. Traceback'i elle biçimlendirmenize gerek kalmaz. Aşağıdaki çıktıda uzun dosya yolunu ornek.py olarak kısalttık; başka bir şey değiştirmedik.
import logging
import sys
logging.basicConfig(level=logging.INFO, stream=sys.stdout, format='%(levelname)s %(message)s')
log = logging.getLogger('odeme')
try:
1 / 0
except ZeroDivisionError:
log.exception('tutar hesaplanamadı')
# çıktı: ERROR tutar hesaplanamadı
# çıktı: Traceback (most recent call last):
# çıktı: File "ornek.py", line 8, in <module>
# çıktı: 1 / 0
# çıktı: ~~^~~
# çıktı: ZeroDivisionError: division by zeroLogger, handler, formatter: bileşenler
HOWTO'nun ileri bölümüne göre logging kütüphanesi modüler bir yapıya sahiptir. Logger'lar uygulama kodunun doğrudan kullandığı arayüzdür. Handler'lar, logger'ların ürettiği kayıtları uygun hedefe gönderir. Filter'lar hangi kaydın çıkacağını daha ince ayarlamaya yarar. Formatter'lar ise kaydın son çıktıdaki düzenini belirler. Olay bilgisi bu bileşenler arasında bir LogRecord nesnesiyle taşınır.
Logger adları noktalarla ayrılmış bir hiyerarşi oluşturur: uygulama adlı logger, uygulama.db ve uygulama.api logger'larının ebeveynidir. Hiyerarşinin tepesinde kök logger vardır. Bu yüzden HOWTO her modülde logging.getLogger(__name__) kullanmayı önerir; logger adları böylece paket ve modül yapısını izler ve bir olayın nereden geldiği addan anlaşılır.
Yayılma (propagate) ve çift satır sorunu
Bir logger'a gelen olay, önce o logger'ın kendi handler'larına, sonra sırasıyla ata logger'ların handler'larına iletilir. logging belgesine göre olay ata logger'ların handler'larına doğrudan gönderilir; ata logger'ların seviye ve filter ayarları dikkate alınmaz. Bir logger'ın propagate özniteliği False ise zincir orada durur. Aşağıdaki örnekte uygulama.db logger'ının kendi handler'ı yok; mesaj ebeveyn uygulama logger'ının handler'ından çıkıyor. Kök logger'a da handler eklenince aynı mesaj iki kez görünüyor; propagate = False ile tekrar bire iniyor.
import logging
import sys
uygulama = logging.getLogger('uygulama')
uygulama.setLevel(logging.INFO)
konsol = logging.StreamHandler(sys.stdout)
konsol.setFormatter(logging.Formatter('[%(name)s] %(levelname)s: %(message)s'))
uygulama.addHandler(konsol)
db = logging.getLogger('uygulama.db')
db.info('sorgu çalıştı')
print(db.parent.name, db.getEffectiveLevel())
logging.basicConfig(stream=sys.stdout, format='KÖK %(name)s: %(message)s')
db.info('aynı mesaj iki kez')
uygulama.propagate = False
db.info('artık bir kez')
# çıktı: [uygulama.db] INFO: sorgu çalıştı
# çıktı: uygulama 20
# çıktı: [uygulama.db] INFO: aynı mesaj iki kez
# çıktı: KÖK uygulama.db: aynı mesaj iki kez
# çıktı: [uygulama.db] INFO: artık bir kezÇift satır sorunu gerçek projelerde sık görülür: hem bir alt logger'a hem kök logger'a handler eklenir ya da aynı handler bir fonksiyon her çağrıldığında yeniden eklenir. Genel kural, handler'ları hiyerarşide tek bir yerde (çoğu zaman yalnızca kökte ya da uygulamanın üst logger'ında) ve programın başlangıcında bir kez eklemektir. Örnekteki getEffectiveLevel() çağrısı da kullanışlı bir ayrıntıyı gösteriyor: kendi seviyesi ayarlanmamış bir logger, seviyesini ebeveyninden alır.
Dosyaya log yazmak ve dosya döndürme
En basit yol basicConfig(filename='uygulama.log', encoding='utf-8', level=logging.INFO) çağrısıdır. HOWTO'ya göre program her çalıştığında mesajlar dosyanın sonuna eklenir; her çalıştırmada temiz bir dosya istiyorsanız filemode='w' verin. encoding argümanı Python 3.9'da eklendi; Türkçe karakterli mesajlar için açıkça 'utf-8' vermek iyi bir alışkanlıktır.
Uzun süre çalışan programlarda log dosyası sürekli büyür. logging.handlers.RotatingFileHandler, belirli bir boyuta (maxBytes) yaklaşıldığında dosyayı kapatıp yenisini açar ve eskilerini .1, .2 gibi uzantılarla saklar. Belgeye göre maxBytes ya da backupCount sıfırsa döndürme hiç yapılmaz; bu yüzden backupCount en az 1, maxBytes sıfırdan büyük olmalıdır. Yazılan dosya her zaman temel addır (örneğin app.log). Zamana göre döndürme için aynı modülde TimedRotatingFileHandler bulunur.
import logging
from logging.handlers import RotatingFileHandler
from pathlib import Path
dosya = Path(__file__).with_name('uygulama.log')
log = logging.getLogger('dosya_ornek')
log.setLevel(logging.INFO)
isleyici = RotatingFileHandler(dosya, maxBytes=1_000_000, backupCount=3, encoding='utf-8')
isleyici.setFormatter(logging.Formatter('%(levelname)s:%(name)s:%(message)s'))
log.addHandler(isleyici)
log.info('Kullanıcı Şükrü giriş yaptı')
log.warning('İzmir şubesi yanıt vermiyor')
isleyici.close()
print(dosya.read_text(encoding='utf-8'), end='')
dosya.unlink()
# çıktı: INFO:dosya_ornek:Kullanıcı Şükrü giriş yaptı
# çıktı: WARNING:dosya_ornek:İzmir şubesi yanıt vermiyorÖrnekteki dosya yolu pathlib ile oluşturuldu; ayrıntılar Python pathlib yazısında. Sunucuda log dosyalarının izinleri ve konumu için Linux nedir yazısı temel bir başlangıç sunar.
dictConfig() ile yapılandırma
HOWTO'ya göre logging üç şekilde yapılandırılabilir: logger, handler ve formatter nesnelerini kodla tek tek oluşturmak, bir yapılandırma dosyasını fileConfig() ile okumak ya da bir sözlüğü dictConfig() fonksiyonuna vermek. Sözlük yöntemi, ayarları JSON ya da YAML gibi bir dosyadan okuyup uygulamanın kodunu değiştirmeden log davranışını ayarlamanızı sağlar. Sözlükte version anahtarı zorunludur ve değeri 1'dir.
import logging
import logging.config
import sys
AYAR = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'kisa': {'format': '%(levelname)s %(name)s: %(message)s'},
},
'handlers': {
'konsol': {
'class': 'logging.StreamHandler',
'formatter': 'kisa',
'stream': 'ext://sys.stdout',
},
},
'loggers': {
'uygulama': {'level': 'DEBUG', 'handlers': ['konsol'], 'propagate': False},
},
'root': {'level': 'WARNING'},
}
logging.config.dictConfig(AYAR)
logging.getLogger('uygulama.api').debug('istek alındı')
logging.getLogger('baska').info('kök WARNING olduğu için görünmez')
# çıktı: DEBUG uygulama.api: istek alındıdisable_existing_loggers ayarına dikkat edin: logging.config belgesine göre bu anahtar verilmezse varsayılan değeri True'dur ve yapılandırma anında var olan kök dışındaki logger'lar devre dışı kalır. Modüllerinizi dictConfig() çağrısından önce içe aktardıysanız, onların logger'ları sessizce susabilir. Bu yüzden örnekte açıkça False verdik. Belgeye göre geçersiz bir seviye ya da boolean olmayan bir propagate değeri gibi hatalarda dictConfig() açıklayıcı bir ValueError, TypeError, AttributeError ya da ImportError yükseltir. JSON dosyalarını okumak için Python json modülü yazısına bakabilirsiniz.
Kütüphane yazıyorsanız
HOWTO'nun "Configuring Logging for a Library" bölümü kütüphane yazarlarına üç öneri verir. Birincisi, kütüphaneniz kök logger'a log yazmamalı; paketinizin adını taşıyan benzersiz bir logger (örneğin __name__) kullanmalı. İkincisi, kütüphanenizin logger'larına NullHandler dışında handler eklememelisiniz; handler seçimi, hedef kitlesini ve ihtiyaçlarını bilen uygulama geliştiricisinin işidir. Üçüncüsü, uygulama logging'i yapılandırmadığında mesajlarınızın stderr'e çıkmasını istemiyorsanız üst logger'ınıza bir NullHandler ekleyebilirsiniz.
import logging
logging.getLogger('benim_kutuphanem').addHandler(logging.NullHandler())
logging.getLogger('benim_kutuphanem.ag').warning('kullanıcı yapılandırmadıkça sessiz')
print('çıktı yok')
# çıktı: çıktı yokHOWTO, yapılandırma yokken WARNING ve üstü mesajların stderr'e yazılmasını en iyi varsayılan davranış olarak görür; yani NullHandler eklemek zorunlu değildir, bilinçli bir tercihtir. Kendi paketinizi yapılandırmak için Python modül ve paket import yazısına, test yazmak için de Python unittest yazısına bakabilirsiniz. unittest modülündeki assertLogs(), bir kod bloğunun beklenen log kaydını üretip üretmediğini doğrular; Python 3.15 What's New belgesine göre bu yöntem artık mesajların nasıl biçimlendirileceğini belirleyen bir formatter da kabul ediyor.
Sürüm notları ve kullanımdan kaldırılanlar
Logger'ın warn() yöntemi Python 3.3'ten beri kullanımdan kaldırılmış durumdadır; What's New belgeleri bunun yerine warning() kullanılmasını hatırlatır. Python 3.15 What's New belgesine göre özel handler'ları strm argümanıyla tanımlamak kullanımdan kaldırıldı ve Python 3.16'da kaldırılması planlanıyor; handler'larınızı stream argümanıyla tanımlayın. Python 3.14'te ise logging.handlers.QueueListener nesneleri context manager protokolünü desteklemeye başladı ve zaten başlatılmış bir dinleyicide start() çağrısı artık RuntimeError yükseltiyor. Diğer değişiklikler için Python 3.15 yenilikleri yazısına bakabilirsiniz.
Sık yapılan hatalar

- INFO mesajlarının görünmemesi: Varsayılan seviye WARNING'dir;
basicConfig(level=logging.INFO)ya dalogger.setLevel()ile ayarlayın. - basicConfig'in ikinci kez çağrılması: Kök logger'da handler varsa etkisizdir; gerekiyorsa
force=Truekullanın. - Aynı mesajın iki kez yazılması: Handler hem alt hem üst logger'a eklenmiştir ya da defalarca eklenmiştir; tek yerde bir kez ekleyin veya
propagateayarını gözden geçirin. - Log çağrısında f-string: Seviye kapalıyken bile metin oluşturulur;
logger.info('%s', deger)yazın. - except bloğunda error() ile traceback'i kaybetmek:
logger.exception()kullanın ya daexc_info=Trueverin. - Kütüphanede basicConfig çağırmak veya kök logger'a yazmak: Yapılandırma uygulamanın işidir.
- dictConfig sonrası susan logger'lar:
disable_existing_loggersvarsayılan olarakTrue'dur.
Sık Sorulan Sorular
logging iş parçacıklarıyla güvenli mi?
Evet; belgeye göre logging modülü, istemcilerin ek bir şey yapmasına gerek kalmadan iş parçacığı açısından güvenli olacak şekilde tasarlanmıştır ve bunu kilitlerle sağlar: modülün paylaşılan verisi için bir kilit vardır, her handler da kendi G/Ç erişimi için ayrı bir kilit oluşturur. Biçim dizesine %(threadName)s ekleyerek mesajın hangi iş parçacığından geldiğini görebilirsiniz. Birden çok süreçten tek bir dosyaya log yazmak ise Logging Cookbook'a göre desteklenmez; Cookbook bu durum için kayıtları tek bir süreçte toplayan çözümler anlatır. İş parçacıkları ve süreçler Python threading ve multiprocessing yazısında.
Log mesajlarına tarih ve saat nasıl eklenir?
Biçim dizesine %(asctime)s ekleyin. Biçimi değiştirmek için datefmt argümanına time.strftime() biçim kodlarını verin, örneğin '%d.%m.%Y %H:%M:%S'.
Hangi seviyeyi seçmeliyim?
Geliştirme sırasında DEBUG, canlı ortamda genellikle INFO ya da WARNING kullanılır. Seviyeyi koda gömmek yerine bir ortam değişkeninden veya komut satırı argümanından okumak, sorun anında kodu değiştirmeden ayrıntıyı artırmanızı sağlar. Ortam değişkenleri için .env dosyası yazısına bakabilirsiniz.
print() çağrılarını logging'e çevirmeli miyim?
Kullanıcıya gösterilen olağan çıktı print() ile kalmalıdır. Tanı ve izleme amacıyla eklediğiniz print() satırlarını ise logger çağrılarına çevirmek, onları silmeden kapatıp açabilmenizi ve zaman, seviye, modül adı gibi bilgileri otomatik eklemenizi sağlar.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Python öğrenme rehberi · try except hata yönetimi · argparse · unittest · threading ve multiprocessing · asyncio