Python 3.15, 9 Ekim 2026 tarihinde yayımlandı ve dilin kendisinde beş büyük yenilik getirdi: lazy anahtar kelimesiyle açık tembel içe aktarma (PEP 810), değiştirilemez sözlük tipi frozendict (PEP 814), yerleşik sentinel tipi (PEP 661), ortamdan bağımsız varsayılan UTF-8 kodlaması (PEP 686) ve comprehension içinde * ile ** açma (PEP 798). Standart kütüphanede yeni profiling paketi ve Tachyon örnekleme profilleyicisi, daha renkli komut satırı çıktıları ve daha yardımcı hata mesajları öne çıkıyor.

Bu yazı yalnızca resmî "What's New in Python 3.15" belgesine dayanır ve önemli değişiklikleri çalışan örneklerle anlatır. Tüm kodlar 3.15 serisinin bu yazı yazılırken en güncel bakım sürümü olan 3.15.2 ile çalıştırıldı. Kaldırılan API'ler, yeni kullanımdan kaldırma uyarıları ve kodunuzu taşırken dikkat etmeniz gereken noktalar da sonda ayrı bölümlerde.

Kısa özet

AlanDeğişiklikKaynak
Dillazy import ve lazy from ... importPEP 810
Yerleşik tipfrozendict: değiştirilemez, hash edilebilir sözlükPEP 814
Yerleşik tipsentinel: benzersiz işaretçi değerlerPEP 661
Kodlamaopen() ve diğer G/Ç için varsayılan UTF-8PEP 686
Söz dizimi[*L for L in listeler] ve {**d for d in sozlukler}PEP 798
Başlangıç.start dosyaları; .pth içindeki import satırları sessizce kullanımdan kalktıPEP 829
Profilprofiling paketi, profiling.sampling (Tachyon)PEP 799
C APIFree-threaded yapılar için kararlı ABI (abi3t)PEP 803
DerlemeFrame pointer varsayılan olarak açıkPEP 831
Tip sistemiTypeForm, TypedDict closed ve extra_items, disjoint_basePEP 747, 728, 800

lazy import: modülü ilk kullanımda yüklemek

lazy import: modülü ilk kullanımda yüklemek

Büyük uygulamaların açılışını yavaşlatan nedenlerden biri içe aktarma sistemidir: her import dosyayı bulur, okur, derler ve üst düzey kodu çalıştırır; o çalıştırmada modül hiç kullanılmasa bile. Geliştiriciler bu yüzden import satırlarını fonksiyonların içine taşımak gibi yollara başvuruyordu. 3.15 ile gelen lazy yumuşak anahtar kelimesi (soft keyword) bu ihtiyacı doğrudan karşılar: import satırı dosyanın başında kalır, ama modül adı ilk kez kullanıldığında yüklenir. O ana kadar ad, hafif bir vekil (proxy) nesneye bağlıdır.

python
lazy import json
lazy from pathlib import Path
import sys

print('json' in sys.modules)
# çıktı: False
veri = json.loads('{"şehir": "Ankara"}')
print(veri['şehir'])
# çıktı: Ankara
print('json' in sys.modules)
# çıktı: True
print(Path('rapor.txt').suffix)
# çıktı: .txt

Vekil nesnenin tipi types.LazyImportType olarak dışa açılmıştır; ad kullanıldığı anda gerçek modülle yer değiştirir:

python
import types
lazy import json

print(type(globals()['json']) is types.LazyImportType)
# çıktı: True
json.dumps(1)
print(type(globals()['json']) is types.LazyImportType)
# çıktı: False

Tembel içe aktarmanın kuralları belgelerde açıkça sayılmış. lazy yalnızca modül düzeyinde kullanılabilir; fonksiyon, sınıf gövdesi ya da try/except/finally bloğu içinde SyntaxError verir. Yıldızlı içe aktarma (lazy from m import *) ve __future__ içe aktarmaları tembel olamaz.

python
def yukle():
    lazy import json
#   File "ornek.py", line 2
#     lazy import json
#     ^^^^^^^^^^^^^^^^
# SyntaxError: lazy import not allowed inside functions

Yükleme başarısız olursa (örneğin modül yoksa) hata import satırında değil, adın ilk kullanıldığı yerde ortaya çıkar. Traceback hem kullanım yerini hem de asıl import satırını gösterir. Yakalanan istisnanın __cause__ özniteliği tembel çözümlemenin hatasını taşır:

python
lazy import olmayan_modul

print('Başladı')
try:
    olmayan_modul.calistir()
except ImportError as e:
    print(type(e).__name__, '|', e)
    print(type(e.__cause__).__name__, '|', e.__cause__)
# çıktı: Başladı
# çıktı: ModuleNotFoundError | No module named 'olmayan_modul'
# çıktı: ImportError | lazy import of 'olmayan_modul' raised an exception during resolution

Kaynak kodu değiştirmeden tüm içe aktarmaları tembel yapmak için -X lazy_imports=all komut satırı seçeneği ya da PYTHON_LAZY_IMPORTS ortam değişkeni vardır; varsayılan değer normal, yalnızca lazy yazılan satırları tembel yapar. Çalışma anında sys.set_lazy_imports() ile mod değiştirilebilir, sys.set_lazy_imports_filter() ile hangi modüllerin tembel yükleneceğine karar veren bir filtre fonksiyonu tanımlanabilir. Eski sürümleri de destekleyen kod için modül düzeyinde __lazy_modules__ = ['json'] listesi tanımlamak, o modüllerin normal import satırlarını tembel hâle getirir. Tembel bir içe aktarma kendini dolaylı yoldan yeniden içe aktarmaya çalışırsa yeni ImportCycleError istisnası fırlatılır. İçe aktarma sisteminin genel işleyişi için modül ve paket import yazısına bakın.

frozendict: değiştirilemez sözlük

frozendict, oluşturulduktan sonra değiştirilemeyen yeni bir yerleşik tiptir. dict sınıfının alt sınıfı değildir, doğrudan object sınıfından türer. Tüm anahtar ve değerleri hash edilebilirse kendisi de hash edilebilir; bu sayede sözlük anahtarı ya da küme elemanı olabilir. Ekleme sırasını korur ama eşitlik karşılaştırması sıraya bakmaz.

python
ayar = frozendict(dil='tr', tema='koyu')
print(ayar)
# çıktı: frozendict({'dil': 'tr', 'tema': 'koyu'})
print(ayar['dil'])
# çıktı: tr
onbellek = {ayar: 'hazır'}
print(onbellek[frozendict(tema='koyu', dil='tr')])
# çıktı: hazır
print(ayar == {'dil': 'tr', 'tema': 'koyu'})
# çıktı: True
print(isinstance(ayar, dict))
# çıktı: False
ayar['dil'] = 'en'
# Traceback (most recent call last):
#   File "ornek.py", line 13, in <module>
#     ayar['dil'] = 'en'
#     ~~~~^^^^^^^
# TypeError: 'frozendict' object does not support item assignment

isinstance(x, dict) kontrolü frozendict için False döndüğünden, sürüm notları bu tür kontrolleri isinstance(x, (dict, frozendict)) ya da her eşleme tipini kabul eden isinstance(x, collections.abc.Mapping) biçimine güncellemeyi önerir. copy, json, pickle, pprint ve decimal gibi modüller frozendict kabul edecek şekilde güncellendi. json modülüne eklenen array_hook parametresiyle birlikte JSON verisini tamamen değiştirilemez bir yapıya çevirebilirsiniz:

python
import json

veri = json.loads('{"ad": "nokta", "konum": [1, 2]}',
                  object_pairs_hook=frozendict, array_hook=tuple)
print(veri)
# çıktı: frozendict({'ad': 'nokta', 'konum': (1, 2)})

Sözlük metotlarının ayrıntıları için Python sözlük metotları yazısına bakabilirsiniz.

sentinel: "değer verilmedi" işaretçisi

Bir parametrenin hiç verilmediğini None değerinden ayırt etmek için uzun zamandır EKSIK = object() gibi özel nesneler kullanılıyordu. Bunların gösterimi anlaşılmazdı ve tip ipuçlarında kullanılamıyordu. Yeni sentinel tipi kısa bir gösterime sahip benzersiz nesneler üretir: kopyalandığında kimliği korunur, tip ifadelerinde | işleciyle kullanılabilir ve modülden adıyla içe aktarılabiliyorsa pickle ile saklanabilir. Belgeler sentinel nesnelerin is ile karşılaştırılmasını, adının atandığı değişkenle aynı olmasını önerir; sentinel her zaman doğru (truthy) kabul edilir ve alt sınıflanamaz.

python
EKSIK = sentinel('EKSIK')

def bul(sozluk, anahtar, varsayilan=EKSIK):
    if anahtar in sozluk:
        return sozluk[anahtar]
    if varsayilan is EKSIK:
        raise KeyError(anahtar)
    return varsayilan

print(EKSIK)
# çıktı: EKSIK
print(bul({'a': None}, 'a'))
# çıktı: None
print(bul({}, 'b', None))
# çıktı: None
print(bool(EKSIK))
# çıktı: True

Bu örnekte None hem geçerli bir sözlük değeri hem de geçerli bir varsayılan olabiliyor; "argüman verilmedi" durumunu ayırmanın tek temiz yolu ayrı bir işaretçidir. Tip ipuçlarında def f(x: int | EKSIK = EKSIK) biçimi kullanılabilir; tip ipuçları için type hints rehberine bakın.

Comprehension içinde * ve ** ile açma

PEP 448 ile gelen açma söz dizimi artık liste, küme ve sözlük comprehension ifadelerinde ve generator ifadelerinde de geçerli. İç içe listeleri düzleştirmek için iki for yazmaya ya da itertools.chain.from_iterable() kullanmaya gerek kalmıyor:

python
siparisler = [['kalem', 'defter'], ['silgi'], []]
print([*s for s in siparisler])
# çıktı: ['kalem', 'defter', 'silgi']

etiketler = [{'python', 'web'}, {'web', 'sunucu'}]
print(sorted({*e for e in etiketler}))
# çıktı: ['python', 'sunucu', 'web']

ayarlar = [{'port': 80}, {'host': 'localhost'}, {'port': 8080}]
print({**a for a in ayarlar})
# çıktı: {'port': 8080, 'host': 'localhost'}

Sözlük örneğinde aynı anahtar birden çok kez geçtiğinde son değer kazanır; port 8080 oldu. Ayrıntılar ve iç içe comprehension ile karşılaştırma için list comprehension yazısına bakabilirsiniz.

Varsayılan kodlama artık UTF-8

3.15 ile Python, sistemin yerel ayarından bağımsız olarak UTF-8 kodlamasını varsayılan kabul ediyor. open('dosya.txt') gibi encoding verilmeyen G/Ç işlemleri artık UTF-8 kullanıyor. Bu değişiklik özellikle Windows'ta önemli; çünkü önceden varsayılan, sistemin kod sayfasıydı. Türkçe Windows kullanan bu bilgisayarda yerel kodlama hâlâ cp1254 görünüyor, ama açılan dosya UTF-8 ile açılıyor:

python
import locale, sys

print(sys.flags.utf8_mode)
# çıktı: 1
print(locale.getencoding())
# çıktı: cp1254
with open('ornek.txt', 'w') as f:
    print(f.encoding)
# çıktı: utf-8

Sürüm notları, sürümler arası en iyi uyumluluk için encoding argümanını her zaman açıkça yazmayı önermeyi sürdürüyor. Yerel kodlamayı bilerek kullanmak isteyenler encoding='locale' yazabilir (3.10'dan beri destekleniyor). Eski davranışa dönmek için PYTHONUTF8=0 ortam değişkeni ya da -X utf8=0 seçeneği kullanılabilir. Konunun Türkçe karakterlerle ilgili tarafı Unicode ve Türkçe karakter, dosya tarafı dosya okuma yazma yazısında.

Daha yardımcı hata mesajları

3.15 ile AttributeError önerileri genişledi. Yerleşik bir tipte yakın bir ad bulunamadığında yorumlayıcı JavaScript, Java, Ruby ve C# gibi dillerdeki yaygın metot adlarından oluşan bir tabloya bakıp Python karşılığını öneriyor. Karşılık bir metot değil de bir dil yapısıysa öneri o yapıyı tarif ediyor; değiştirilemez bir tipte değiştiren bir metot çağrıldığında değiştirilebilir karşılığı öneriliyor:

python
ayar = {}
ayar.put('dil', 'tr')
# Traceback (most recent call last):
#   File "ornek.py", line 2, in <module>
#     ayar.put('dil', 'tr')
#     ^^^^^^^^
# AttributeError: 'dict' object has no attribute 'put'. Use d[k] = v.
python
renkler = ('kırmızı', 'mavi')
renkler.append('yeşil')
# Traceback (most recent call last):
#   File "ornek.py", line 2, in <module>
#     renkler.append('yeşil')
#     ^^^^^^^^^^^^^^
# AttributeError: 'tuple' object has no attribute 'append'. Did you mean to use a 'list' object?

Ayrıca bir nesnenin özniteliği üzerinden erişilebilen bir ad istendiğinde öneri '.inner.area' gibi iç yolu gösteriyor; delattr() ve del nesne.ad başarısız olduğunda da benzer ad önerisi geliyor. Önerilerin, istisna nesnesinin str() değerinde değil traceback görüntülenirken eklendiğini bilmekte fayda var: except AttributeError as e: print(e) yazdığınızda öneri satırı görünmez. Bu ve önceki sürümlerdeki mesajların tamamı için sık hata mesajları yazısına bakın.

Standart kütüphanede öne çıkanlar

Standart kütüphanede öne çıkanlar: profiling paketi, argparse, re.match() adının kafa karıştırıcı olması nedeniyle aynı işi yapan re.prefixmatch() eklendi ve match()...
  • profiling paketi: profiling.tracing (eski cProfile) ve yeni örnekleme profilleyicisi profiling.sampling (Tachyon) tek ad alanında toplandı. Tachyon çalışan bir sürece PID ile bağlanabiliyor, duvar saati, CPU, GIL ve istisna modlarında örnek topluyor; flame graph, heatmap ve Firefox Profiler biçiminde çıktı üretebiliyor. cProfile geriye uyumluluk için takma ad olarak kalıyor; profile modülü kullanımdan kaldırıldı ve 3.17'de silinecek.
  • argparse: yanlış yazılan seçimler için öneri varsayılan olarak açık; ayrıntılar argparse rehberinde.
  • re: re.match() adının kafa karıştırıcı olması nedeniyle aynı işi yapan re.prefixmatch() eklendi ve match() yumuşak kullanımdan kaldırıldı (soft deprecated). Eski ad silinmeyecek.
  • unicodedata: Unicode veritabanı 17.0.0 sürümüne güncellendi; block(), iter_graphemes(), isxidstart() ve isxidcontinue() eklendi.
  • math.integer: tam sayı argümanlı matematik fonksiyonları için yeni modül (PEP 791).
  • collections: Counter nesneleri için simetrik fark işleci ^ eklendi.
  • asyncio: TaskGroup.cancel() ile bir görev grubu erkenden sonlandırılabiliyor.
  • unittest: assertLogs() bir formatter alabiliyor; assertWarns() eşleşmeyen uyarıları artık yutmuyor.
  • Daha fazla renk: argparse, sqlite3 kabuğu, timeit, difflib, http.server, python --help ve birçok modülün komut satırı yardımı renkli.
python
import re

print(re.prefixmatch(r'\d+', '2026 yılı'))
# çıktı: <re.Match object; span=(0, 4), match='2026'>
print(re.prefixmatch(r'\d+', 'yıl 2026'))
# çıktı: None
print(re.search(r'\d+', 'yıl 2026'))
# çıktı: <re.Match object; span=(4, 8), match='2026'>
python
import unicodedata
from math import integer
from collections import Counter

print(unicodedata.unidata_version)
# çıktı: 17.0.0
print(unicodedata.block('ğ'))
# çıktı: Latin Extended-A
print(integer.isqrt(50))
# çıktı: 7
print(Counter(elma=3, armut=1) ^ Counter(elma=1, kiraz=2))
# çıktı: Counter({'elma': 2, 'kiraz': 2, 'armut': 1})

prefixmatch() yalnızca metnin başından eşleşme arar, search() metnin her yerine bakar. Belgeler eski sürümleri de destekleyen kodda match() kullanmayı sürdürmeyi, yalnızca 3.15 hedefleyen yeni kodda prefixmatch() yazmayı önerir. Düzenli ifadelerin geri kalanı için re modülü yazısına bakabilirsiniz.

Kaldırılanlar ve kodunuzu bozabilecek değişiklikler

Kaldırılanlar ve kodunuzu bozabilecek değişiklikler: datetime, sqlite3, argparse, base64

Önceki sürümlerde kullanımdan kaldırılmak üzere işaretlenen bazı API'ler 3.15 ile silindi ya da hata vermeye başladı. Kodunuzu yükseltmeden önce özellikle şunlara bakın:

AlanDeğişiklik
datetimestrptime() biçiminde yıl olmadan %d kullanmak artık ValueError
sqlite3connect() fonksiyonunda database dışındaki tüm parametreler yalnızca anahtar kelimeyle
argparseadd_argument('-f', '-foo') için dest artık 'foo'
base64urlsafe_b64decode() artık dolgu (padding) istemiyor
http.serverCGIHTTPRequestHandler ve --cgi seçeneği kaldırıldı
pathlibPurePath.is_reserved() kaldırıldı; os.path.isreserved() kullanın
globBelgelenmemiş glob0() ve glob1() kaldırıldı
sre_*sre_compile, sre_constants, sre_parse modülleri kaldırıldı
typingNamedTuple için anahtar kelime söz dizimi ve alansız TypedDict("TD") biçimi kaldırıldı
importlibLoader.load_module() kaldırıldı; exec_module() kullanın
modüller__cached__ özniteliği artık ayarlanmıyor; __spec__.cached kullanın
python
from datetime import datetime

print(datetime.strptime('2026-11-07', '%Y-%m-%d').date())
# çıktı: 2026-11-07
datetime.strptime('07.11', '%d.%m')
# ValueError: Day of month directive '%d' may not be used without a year directive. Parsing dates involving a day of month without a year is ambiguous and fails to parse leap day. Add a year to the input and format. See https://github.com/python/cpython/issues/70647.
python
import sqlite3

baglanti = sqlite3.connect(':memory:', 5.0)
# TypeError: connect() takes at most 1 positional arguments (2 given)

Bu iki örnekte traceback'in yalnızca son satırını gösterdik. sqlite3 için çözüm zaman aşımını sqlite3.connect(':memory:', timeout=5.0) biçiminde anahtar kelimeyle vermektir; modülün geri kalanı sqlite3 rehberinde. Tarih ayrıştırmada yıl içermeyen girdiye bir yıl eklemek gerekir; ayrıntılar datetime yazısında.

Yeni kullanımdan kaldırma uyarıları

  • profile modülü 3.17 sürümünde kaldırılacak; yerine profiling.tracing kullanın.
  • -b ve -bb komut satırı seçenekleri 3.17 ile etkisiz hâle gelecek.
  • argparse, csv, json, logging, re, pickle gibi birçok standart modülün __version__, version ve VERSION öznitelikleri 3.20 sürümünde kaldırılacak; sürüm için sys.version_info kullanın.
  • hashlib yapıcılarında string= anahtar kelimesi 3.19 sürümünde kaldırılacak.
  • collections.abc.ByteString ve typing.ByteString artık içe aktarıldığında bile DeprecationWarning veriyor; 3.17 sürümünde kaldırılacaklar.
  • .pth dosyalarındaki import satırları sessizce kullanımdan kaldırıldı; aynı adlı .start dosyası varsa yok sayılıyorlar.

Tüm liste için resmî Deprecations sayfası güncel tutuluyor.

Yorumlayıcı, derleme ve dağıtım tarafı

Deneysel JIT derleyicisi önemli ölçüde güncellendi; sürüm notları pyperformance kıyaslamalarında x86-64 Linux üzerinde yüzde 7-8, AArch64 macOS üzerinde yüzde 11-12 geometrik ortalama hızlanma raporluyor. Resmî Windows 64 bit ikili dosyaları artık Visual Studio 2026 ile gelen kuyruk çağrılı (tail-calling) yorumlayıcıyı kullanıyor; macOS ikili dosyaları free-threading desteğini varsayılan olarak kuruyor. CPython artık destekleyen platformlarda frame pointer ile derleniyor; bu, sistem profilleyicileri ve hata ayıklayıcıların yığın izini daha güvenilir çıkarmasını sağlıyor. C eklentisi yazanlar için free-threaded yapılarla uyumlu kararlı ABI (abi3t) geldi. Free-threading konusunun genel çerçevesi için threading ve multiprocessing yazısına bakabilirsiniz.

3.15.2 bakım sürümünde iki önemli düzeltme öne çıkıyor: paket ve alt modülleri aynı anda ilk kez içe aktaran iş parçacıkları arasında 3.15.0 ile ortaya çıkan kilitlenme (deadlock) riski giderildi; ssl modülü ise ele geçirilmiş belirli eMudhra ara sertifikalarını içeren zincirleri, uygulama kendi kök sertifika paketini verse bile reddediyor.

Yükseltme kontrol listesi

  • Projeniz için yeni bir sanal ortamı 3.15 ile oluşturun ve testlerinizi çalıştırın; unittest çıktısında yeni görünen uyarılara bakın.
  • encoding vermeden dosya açtığınız yerleri bulun; Windows'ta eski kod sayfasıyla yazılmış dosyaları okuyorsanız encoding değerini açıkça yazın.
  • isinstance(x, dict) kontrollerinin frozendict ile ilişkisini değerlendirin.
  • sqlite3.connect(), strptime() ve argparse dest değişikliklerinin kodunuzu etkileyip etkilemediğini kontrol edin.
  • python -W error::DeprecationWarning ile çalıştırıp kullanımdan kalkan API'leri erkenden yakalayın.
  • Açılış süresi önemliyse ağır modülleri lazy import ile işaretlemeyi deneyin.

Sık Sorulan Sorular

Python 3.15 ne zaman çıktı?

Sürüm notlarına göre 9 Ekim 2026 tarihinde. Bu yazı 3.15.2 bakım sürümüyle denendi.

lazy import her projede kullanılmalı mı?

Zorunlu değil. Belgeler bu özelliğin özellikle çok sayıda modülü üst düzeyde içe aktaran ama her çalıştırmada bunların bir kısmını kullanan uygulamalarda açılış gecikmesini azalttığını belirtir. Hatanın import satırında değil ilk kullanımda çıkacağını unutmayın.

frozendict ile MappingProxyType arasındaki fark nedir?

types.MappingProxyType var olan bir sözlüğün salt okunur görünümüdür; alttaki sözlük değişirse görünüm de değişir. frozendict ise kendi verisini tutan, oluşturulduktan sonra değişmeyen bağımsız bir tiptir ve hash edilebilir olabilir.

Kodum 3.15 öncesi sürümlerde de çalışmalı, ne yapmalıyım?

lazy anahtar kelimesi, frozendict, sentinel ve comprehension içinde açma eski sürümlerde söz dizimi ya da ad hatası verir. Tembel içe aktarma için belgelerin önerdiği __lazy_modules__ listesi eski sürümlerde sıradan bir değişken olarak kalır; diğer özellikler için sürüm kontrolü ya da eski yazım biçimleri gerekir.

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 · Sık hata mesajları · Unicode ve UTF-8 · Sözlük metotları · List comprehension · Modül ve import