Python mutable default argument hatasını düzeltmek için liste veya sözlük varsayılanı yerine None kullanmalı, fonksiyon içinde is None kontrolüyle yeni bir liste ya da sözlük oluşturmalısın. Sorunun kaynağı, etiketler=[] veya veriler={} yazıldığında nesnenin fonksiyon tanımı çalıştırılırken oluşturulması ve ilgili argüman verilmediğinde çağrılar arasında yeniden kullanılmasıdır. Bu paylaşılan nesneyi yerinde değiştirdiğinde, bir çağrının eklediği veri sonraki çağrıya taşınır.
Python Mutable Default Argument Hatası Neden Oluşur?
Tanım zamanı, Python'ın def ifadesini çalıştırıp fonksiyonu oluşturduğu aşamadır. İmzadaki boş liste veya sözlük bu aşamada oluşturulur. Çağrı zamanı ise fonksiyonu kullanıp gövdesindeki işlemleri yürüttüğün aşamadır; varsayılan ifade her çağrıda yeniden değerlendirilmez.
Aşağıdaki örnekte amaç, verilen etiketi listeye eklemektir. Ancak varsayılan listenin her çağrıda boş başlayacağını düşünürsen sonuç şaşırtıcı olur:
def etiket_ekle(etiket, etiketler=[]):
etiketler.append(etiket)
return etiketler
print(etiket_ekle('python'))
print(etiket_ekle('fonksiyon'))
Beklenen çıktı:
['python']
['python', 'fonksiyon']
İlk çağrıda etiketler argümanı verilmediği için varsayılan liste kullanılır. append, bu listeye 'python' değerini ekler. İkinci çağrıda da argüman verilmez; fakat kullanılan liste artık boş değildir. Aynı listeye 'fonksiyon' eklendiğinden iki etiket birlikte görünür.
Burada Python bir hata mesajı üretmez. Kod sözdizimi açısından geçerlidir ve tanımlanan işlemleri gerçekleştirir. “Hata” dediğimiz şey, çağrıların birbirinden bağımsız çalışmasını beklerken önceki çağrının verisini sonraki sonuçta görmendir. Varsayılan nesneyi paylaşmak dilin davranışıdır; bunun istenmeyen bir sonuç olup olmadığı fonksiyonun amacına bağlıdır.
Liste ve sözlük neden sayı ve metinden farklı davranır?
Liste ve sözlük, mutable, yani değiştirilebilir nesnelerdir. Listeye eleman eklemek veya sözlükte bir anahtarın değerini değiştirmek, mevcut nesneyi yerinde değiştirir. Bu yüzden aynı nesneyi kullanan sonraki çağrı değişiklikleri görür.
Sayılar ve metinler ise immutable, yani değiştirilemez değerlerdir. Örneğin sayi = sayi + 1 işleminde mevcut sayı değiştirilmez; toplamanın sonucunda elde edilen değere sayi adı bağlanır. Metin birleştirmede de mevcut metni yerinde değiştirmek yerine yeni bir değer elde edilir. Dolayısıyla bu işlemler, paylaşılan bir listeye append uygulamakla aynı etkiyi yaratmaz.
Fonksiyon İmzasını Okuyarak Hata Nasıl Teşhis Edilir?

İlk bakacağın yer, def satırında parametrelerin yazıldığı fonksiyon imzasıdır. parametre=[] ve parametre={} biçimleri, çağrılar arasında paylaşılabilecek değiştirilebilir bir varsayılan nesne bulunduğunu gösterir. Teşhisi üç soruyla ilerletebilirsin.
Varsayılan ifade ne?
etiket_ekle örneğinde etiket parametresinin varsayılan değeri yoktur; çağrıda sağlanması gerekir. etiketler parametresinin varsayılan ifadesi ise [] şeklindedir. Bir parametrenin liste kabul etmesi tek başına bu sorunu oluşturmaz; önemli olan, verilmediğinde hangi nesnenin kullanılacağıdır.
Değiştirilebilir bir varsayılan görmek de tek başına veri biriktiğini kanıtlamaz. Gövdede append, update veya öğe ataması gibi mevcut nesneyi değiştiren işlemler bulunup bulunmadığına bakmalısın.
Bu ifade ne zaman değerlendiriliyor?
İmzadaki varsayılan ifade, fonksiyon tanımı çalıştırılırken değerlendirilir. Buna karşılık fonksiyon gövdesine yazılan bir liste oluşturma ifadesi, yürütme o satıra her ulaştığında çalışır. Bu ayrım, neden yalnızca varsayılanı seçmenin değil, yeni nesnenin nerede oluşturulduğunun da önemli olduğunu açıklar.
Argüman verilmediğinde aynı nesne mi kullanılıyor?
Evet. Ancak çağrıda açıkça bir nesne verirsen o çağrı için varsayılan nesne kullanılmaz. Aşağıdaki sözlük örneğinde her çağrıya ayrı bir boş sözlük gönderiliyor:
def kayit_ekle(anahtar, deger, veriler={}):
veriler[anahtar] = deger
return veriler
print(kayit_ekle('dil', 'Python', veriler={}))
print(kayit_ekle('konu', 'fonksiyon', veriler={}))
Beklenen çıktı:
{'dil': 'Python'}
{'konu': 'fonksiyon'}
Çağrı ifadelerindeki iki ayrı {}, iki bağımsız sözlük oluşturur. Bu nedenle ikinci sonuçta ilk kaydın verisi bulunmaz. Ancak fonksiyonun varsayılan değer sorunu düzelmiş değildir: veriler argümanını vermeden çağırırsan imzadaki paylaşılan sözlük yine kullanılır.
Ayrıca aynı listeyi veya sözlüğü bir değişkende tutup iki çağrıya da açıkça gönderirsen yine aynı nesne üzerinde çalışırsın. Bu, varsayılan argümandan kaynaklanan paylaşım değildir; çağıran kodun aynı nesneyi yeniden vermesinin sonucudur.
Temel Python bilgilerini değerlendirmek ve hangi konulara yeniden çalışacağına karar vermek için Python bilgi testi ile kendini sınayabilirsin.
Hatalı ve Güvenli Sürümleri Aynı Çağrı Dizisinde Karşılaştırın

Mutable varsayılan argüman hatasını görmek için aynı çağrı dizisini iki farklı sürümde çalıştırabiliriz. Hatalı sürümde liste, fonksiyon her çağrıldığında değil, def satırı çalıştığında bir kez oluşturulur. Sonraki çağrılar aynı liste nesnesini kullanmaya devam eder.
def etiket_ekle(etiket, etiketler=[]):
etiketler.append(etiket)
return etiketler
print(etiket_ekle('python'))
print(etiket_ekle('fonksiyon'))
Hatalı sürümün konsol çıktısı:
['python']
['python', 'fonksiyon']
İkinci çağrıda yalnızca 'fonksiyon' etiketini bekleyebilirsin. Ancak ilk çağrıda eklenen 'python' etiketi aynı liste içinde kaldığı için ikinci çıktı önceki değeri de taşır.
def etiket_ekle(etiket, etiketler=None):
if etiketler is None:
etiketler = []
etiketler.append(etiket)
return etiketler
print(etiket_ekle('python'))
print(etiket_ekle('fonksiyon'))
Güvenli sürümün konsol çıktısı:
['python']
['fonksiyon']
Bu kez None yalnızca “liste verilmedi” anlamına gelir. Her çağrıda etiketler is None koşulu sağlanır ve fonksiyon gövdesi içinde yeni bir liste oluşturulur.
| sürüm | çağrı | beklenen çıktı | neden |
|---|---|---|---|
| Hatalı | etiket_ekle('python') |
['python'] |
Varsayılan listeye ilk etiket eklenir. |
| Hatalı | etiket_ekle('fonksiyon') |
['python', 'fonksiyon'] |
Tanım sırasında oluşturulan aynı liste yeniden kullanılır. |
| Güvenli | etiket_ekle('python') |
['python'] |
Çağrı sırasında yeni bir liste oluşturulur. |
| Güvenli | etiket_ekle('fonksiyon') |
['fonksiyon'] |
Önceki çağrıdan bağımsız yeni bir liste kullanılır. |
None Kontrolüyle Liste ve Sözlük Parametreleri Nasıl Güvenli Yazılır?
Liste parametrelerinde güvenli kalıp, varsayılan değeri None yapmak ve gerçek listeyi fonksiyon çağrısı sırasında oluşturmaktır. Böylece ardışık çağrılar birbirinin verisini taşımaz.
def etiket_ekle(etiket, etiketler=None):
if etiketler is None:
etiketler = []
etiketler.append(etiket)
return etiketler
print(etiket_ekle('python'))
print(etiket_ekle('fonksiyon'))
Beklenen çıktı:
['python']
['fonksiyon']
Aynı yaklaşım sözlük parametrelerinde de kullanılır. Aşağıdaki fonksiyon, kullanıcı adına göre sayaç artırır:
def sayac_artir(kullanici, sayaclar=None):
if sayaclar is None:
sayaclar = {}
sayaclar[kullanici] = sayaclar.get(kullanici, 0) + 1
return sayaclar
print(sayac_artir('Ada'))
print(sayac_artir('Deniz'))
ortak_sayac = {}
print(sayac_artir('Ada', ortak_sayac))
print(sayac_artir('Ada', ortak_sayac))
Beklenen çıktı:
{'Ada': 1}
{'Deniz': 1}
{'Ada': 1}
{'Ada': 2}
İlk iki çağrıda sözlük parametresi verilmediği için her çağrıda yeni bir sözlük oluşturulur. Son iki çağrıda ise aynı ortak_sayac sözlüğü bilinçli olarak gönderilir; bu nedenle ikinci çağrı, ilk çağrının sayacını artırır. Bu konuyu farklı liste ve sözlük örnekleriyle uygulamalı pekiştirmek istersen özel Python eğitimi hakkında bilgi edinebilirsin.
Kod İncelemesi İçin Mutable Default Argument Kontrol Listesi
Bir fonksiyonu incelerken aşağıdaki adımlar, mutable default argument hatasını erken fark etmenize yardımcı olur:
- Fonksiyon imzasını tarayın.
items=[],options={}veyavalues=set()gibi değiştirilebilir nesnelerin varsayılan değer olarak kullanılıp kullanılmadığını kontrol edin. Özel sınıflardan oluşturulan değiştirilebilir nesneler de aynı riski taşıyabilir. - Varsayılan ifadenin ne zaman değerlendirildiğini hatırlayın. Varsayılan değer, fonksiyon her çağrıldığında değil, fonksiyon tanımlandığında oluşturulur. Bu nedenle fonksiyon içinde listeyi ya da sözlüğü değiştiren işlemler, sonraki çağrılara yansıyabilir.
- Parametresiz çağrıyı iki kez deneyin. Aynı fonksiyonu arka arkaya parametre vermeden çağırın ve çıktıları karşılaştırın. İkinci çıktıda ilk çağrının eklediği veriler görünüyorsa varsayılan nesne çağrılar arasında paylaşılıyor olabilir.
- Bağımsız nesne gerekip gerekmediğini belirleyin. Her çağrının temiz bir liste veya sözlükle başlaması gerekiyorsa
Nonekullanın ve kontrolüis Noneile yapın:if items is None: items = []. Sözlük için de aynı yaklaşımoptionsdeğişkeniyle uygulanabilir. - İki farklı çağrı biçimini test edin. Parametreyi tamamen atlayan çağrıyı ve açıkça boş liste ya da sözlük gönderen çağrıyı ayrı ayrı deneyin. Böylece hem varsayılan değer davranışını hem de çağıranın gönderdiği nesnenin değiştirilip değiştirilmediğini görebilirsiniz.
- Paylaşımın bilinçli olup olmadığını doğrulayın. Listenin veya sözlüğün çağrılar arasında korunması gerçekten isteniyorsa bunu tesadüfi bir varsayılan değerle gizlemeyin. Paylaşılan durumu açıkça adlandırın, amacını belgeleyin ve bu davranışı testlerle güvence altına alın.
Bu kontrol listesini algoritmik düşünme ve temel kodlama bilgisiyle birlikte uygulamak için ücretsiz kodlama testi üzerinden pratik yapabilirsiniz.
Sık Sorulan Sorular
Mutable default argument yalnızca listelerde mi görülür?
Hayır. Sözlükler, kümeler, bytearray nesneleri ve değiştirilebilir özel sınıf nesneleri de aynı davranışa yol açabilir. Sorun, belirli bir veri türünden çok, nesnenin değiştirilebilir olmasıyla ilgilidir.
None yerine her çağrıda boş liste veya sözlük göndermek aynı sonucu verir mi?
Tam olarak aynı değildir. func([]) her çağrıda yeni bir liste oluşturabilir; ancak fonksiyon bu listeyi değiştirdiğinde çağıranın elindeki nesne de etkilenir. None kalıbı ise varsayılan durumda nesneyi fonksiyon içinde oluşturur ve parametrenin atlanmasıyla açıkça gönderilmesini birbirinden ayırır.
Bir listenin fonksiyon çağrıları arasında korunmasını bilerek istiyorsam nasıl ilerlemeliyim?
Bu paylaşımı açıkça tasarlayın ve belgeleyin. Durumu adlandırılmış bir değişken, fonksiyon özelliği veya uygun bir sınıf üzerinden yönetmek, amacın kodu inceleyen kişi tarafından daha kolay anlaşılmasını sağlar.
Bu adımlar, mutable default argument kaynaklı beklenmedik davranışları kod incelemesi sırasında daha hızlı yakalamanızı sağlar.