Python'da shallow copy (sığ kopya) yalnızca en dış kabı kopyalarken içindeki nesnelere hâlâ aynı referansla işaret eder; deep copy (derin kopya) ise iç içe geçmiş tüm nesneleri özyinelemeli olarak yeniden üretir ve tamamen bağımsız bir veri seti oluşturur. Bu fark özellikle nested liste veya sözlük gibi mutable yapılarla çalışırken kritik hale gelir: shallow copy ile yapılan bir değişiklik, orijinal veriyi de değiştirebilir. Bu yazıda id() fonksiyonundan copy modülüne, hata senaryolarından doğru çözüme kadar konuyu adım adım ele alıyoruz.
Python'da = Ataması ve Nesne Kimliği (id()) Ne Anlama Gelir?
Python'da bir değişken, bir veriyi doğrudan "içinde tutmaz"; bellekte oluşturulmuş bir nesneye referans (işaretçi) tutar. = operatörü ile yapılan atama işlemi bu yüzden veri kopyalamaz, sadece aynı nesneye ikinci bir isim (etiket) bağlar. İki değişken aynı nesneyi işaret ettiğinde, biri üzerinden yapılan değişiklik diğerinde de görünür — çünkü aslında değişen tek bir nesnedir, iki farklı kopya değil.
Bu davranışı somut görmek için Python'un her nesneye atadığı benzersiz kimlik numarasını döndüren id() fonksiyonu kullanılır. İki değişkenin id() değerleri eşitse, aynı nesneyi işaret ediyorlar demektir:
liste_a = [1, 2, 3]
liste_b = liste_a
print(id(liste_a) == id(liste_b)) # True
liste_b.append(4)
print(liste_a) # [1, 2, 3, 4] -- liste_a da değişti
Burada liste_b, liste_a'nın bir kopyası değil, aynı listeye verilmiş ikinci bir isimdir. liste_b üzerinde yapılan append işlemi, tek bir nesne üzerinde gerçekleştiği için liste_a üzerinden de görünür hale gelir.
Bu noktada Python'daki nesneleri iki gruba ayırmak gerekir: mutable (değiştirilebilir) ve immutable (değiştirilemez) nesneler. list, dict ve set gibi mutable nesneler yerinde değiştirilebilir; bir referans üzerinden yapılan ekleme, silme veya güncelleme, o nesneyi işaret eden tüm değişkenlerde etkili olur. int, str, float ve tuple gibi immutable nesnelerde ise "değiştirme" aslında yeni bir nesne oluşturup değişkeni ona yeniden bağlamak anlamına gelir, dolayısıyla referans paylaşımı pratikte sorun yaratmaz. Kopyalama hatalarının neredeyse tamamı, mutable nesnelerin referans yoluyla paylaşılmasından kaynaklanır — ilerleyen bölümlerde shallow ve deep copy ayrımının tam olarak burada devreye girdiğini göreceğiz.
copy Modülü: copy() ve deepcopy() Nasıl Çalışır?
Referans paylaşımının istenmeyen sonuçlarını önlemek için Python'un standart kütüphanesinde copy modülü bulunur. Bu modülün amacı, = atamasının aksine gerçekten yeni bir nesne üreterek orijinal veriden bağımsız bir kopya elde etmektir. Modül iki temel fonksiyon sunar: copy.copy() ve copy.deepcopy().
copy.copy() bir shallow copy (sığ kopya) üretir: yalnızca en üst seviyedeki kabı (listenin, sözlüğün kendisini) yeni bir nesne olarak kopyalar; ancak bu kabın içinde başka mutable nesneler varsa, onlara olan referansları aynen kopyalar, yeni birer nesne üretmez. Yani dış kap yeni, iç elemanlar hâlâ eskisiyle aynıdır:
import copy
orijinal = [1, 2, [3, 4]]
sig_kopya = copy.copy(orijinal)
print(id(orijinal) == id(sig_kopya)) # False
print(id(orijinal[2]) == id(sig_kopya[2])) # True
Buna karşılık copy.deepcopy(), kabın içindeki tüm nesneleri de özyinelemeli (recursive) biçimde dolaşarak her birinin ayrı bir kopyasını oluşturur. Sonuçta iç içe geçmiş her seviye, orijinalden tamamen bağımsız hale gelir:
derin_kopya = copy.deepcopy(orijinal)
print(id(orijinal[2]) == id(derin_kopya[2])) # False
Ayrıca list.copy() ve dict.copy() gibi yerleşik metotların da copy.copy() ile aynı mantıkta çalıştığını, yani shallow copy anlamına geldiğini belirtmek gerekir; bu metotlar sadece dış kabı çoğaltır, iç nesneleri paylaşmaya devam eder. Bu fonksiyonların ayrımını kod üzerinde defalarca tekrar ederek pekiştirmek, Python öğreniminde en kalıcı yöntemdir; bu tür alıştırmaları düzenli uygulamak isteyenler Python özel dersi kapsamında rehberli pratik yapabilir.
Shallow Copy Nested Yapılarda Neden Veri Paylaşımına Yol Açar?

Sorun tam olarak burada başlar: bir liste veya sözlük yalnızca sayı, string gibi immutable elemanlar içeriyorsa shallow copy sorunsuz çalışır, çünkü paylaşılan referanslar zaten "değiştirilemez" nesnelerdir. Ancak yapı nested (iç içe) ise, yani bir listenin elemanı başka bir liste veya sözlükse, shallow copy dış kabı yeni bir nesne yapar ama alt listeler/sözlükler orijinaldeki ile aynı referansı taşımaya devam eder. Bu durumda kopya üzerinde alt elemana yapılan bir değişiklik, orijinal veride de görünür.
Bu kavramsal farkı netleştirmek için shallow copy ile deep copy'nin nested yapılardaki davranışını karşılaştırmak faydalıdır:
- Shallow copy alt nesneleri (nested liste, sözlük) orijinalle paylaşır; deep copy ise her seviyedeki alt nesneyi de ayrı ayrı çoğaltır.
- Düz (flat) bir listede — yani sadece sayı, string gibi elemanlar içeren bir listede — shallow copy tamamen güvenlidir, çünkü paylaşılacak mutable bir alt nesne yoktur.
- Nested bir listede shallow copy risklidir; alt listeye yapılan bir
appendveya değer değişikliği, farkında olmadan orijinal veriyi de bozabilir. - Deep copy her seviyeyi bağımsız kopyaladığı için ekstra nesne oluşturma maliyeti taşır; shallow copy ise yalnızca dış kabı kopyaladığından daha hafif bir işlemdir.
- Hangi yöntemin uygun olduğu, verinin "düz" mü yoksa "iç içe" mi olduğuna bakılarak belirlenmelidir; bu ayrımı yapmadan seçilen kopyalama yöntemi ileride tespit edilmesi zor hatalara yol açabilir.
Bu noktada kendi bilgi seviyenizi ölçmek isterseniz, mutable/immutable ayrımı ve referans mantığı gibi temel konuları içeren Python bilgi testi ile hızlı bir öz değerlendirme yapabilirsiniz. Bir sonraki bölümde bu riskin somut bir kod senaryosu üzerinde nasıl ortaya çıktığını ve nasıl fark edileceğini adım adım inceleyeceğiz.
Adım Adım Hata Senaryosu: Nested Listeyi Shallow Copy ile Kopyalamak
Shallow copy'nin nested (iç içe) yapılarda nasıl sorun çıkardığını görmenin en net yolu, adım adım bir senaryo kurmaktır. Aşağıdaki sırayı takip ettiğinde, copy.copy() fonksiyonunun neden "kopya" beklentisini karşılamadığını somut olarak görebilirsin.
- İçinde başka listeler barındıran bir nested liste oluştur.
copy.copy()kullanarak bu listenin bir kopyasını al.- Kopyadaki alt listelerden birine yeni bir eleman ekle veya mevcut bir elemanı değiştir.
- Orijinal listeyi tekrar ekrana yazdırarak değişip değişmediğini kontrol et.
id()fonksiyonuyla orijinal ve kopyadaki alt listelerin bellekte aynı nesneyi işaret edip etmediğini doğrula.
Bu adımları kodla ifade edersek:
import copy
orijinal = [[1, 2, 3], [4, 5, 6]]
kopya = copy.copy(orijinal)
kopya[0].append(99)
print("Orijinal:", orijinal)
print("Kopya:", kopya)
print("Alt liste id eşit mi?", id(orijinal[0]) == id(kopya[0]))
Bu kodu çalıştırdığında, yalnızca kopya[0] listesine eleman eklemene rağmen orijinal[0] listesinin de değiştiğini görürsün. Çünkü copy.copy() yalnızca dış listenin kendisini yeni bir nesne olarak oluşturur; içindeki alt listeler için referans kopyalar, yani orijinal alt listelerin adresini kopyaya da yazar. id() karşılaştırması bunu doğrular: iki alt liste aynı nesneyi gösterdiği için sonuç True döner.
Bu davranış geliştiriciler tarafından sıkça "beklenmedik veri değişimi" olarak tanımlanır, çünkü kod satırında açıkça sadece kopya değişkeni üzerinde işlem yapılmıştır; orijinal veriye dokunulmamış gibi görünür. Ancak Python'da liste içindeki listeler birer mutable nesne olduğundan, dış listenin kopyalanması onların da bağımsızlaşacağı anlamına gelmez. Özellikle fonksiyonlara parametre olarak geçirilen nested veri yapılarında bu hata, çağıran kodun hiç haberi olmadan orijinal veriyi bozabilir ve hata ayıklaması en zor senaryolardan birine dönüşür.
Doğru Çözüm: deepcopy ile Nested Veriyi Bağımsız Kopyalamak

Yukarıdaki sorunun çözümü, copy modülünün sunduğu deepcopy() fonksiyonunu kullanmaktır. Aynı senaryoyu bu kez copy.deepcopy() ile kurduğunda, dış listenin yanı sıra içindeki her alt listenin de yeni ve bağımsız bir nesne olarak oluşturulduğunu görürsün.
import copy
orijinal = [[1, 2, 3], [4, 5, 6]]
kopya = copy.deepcopy(orijinal)
kopya[0].append(99)
print("Orijinal:", orijinal)
print("Kopya:", kopya)
print("Alt liste id eşit mi?", id(orijinal[0]) == id(kopya[0]))
Bu kez çıktı beklendiği gibidir: kopya[0] listesine eklenen eleman yalnızca kopyada görünür, orijinal değişmeden kalır. id() karşılaştırması da bunu doğrular ve sonuç False döner; çünkü deepcopy() artık orijinal alt listenin referansını değil, onun tamamen ayrı bir kopyasını oluşturmuştur. Aynı mantık sözlük içinde liste, liste içinde sözlük gibi daha karmaşık iç içe yapılar için de geçerlidir; deepcopy() yapının her katmanını tek tek dolaşarak bağımsız nesneler üretir.
Nested veri yapılarıyla çalışırken hangi kopyalama fonksiyonunu seçeceğini netleştirmek, ileride ortaya çıkabilecek veri tutarsızlıklarını baştan engeller. Bu konuyu ve Python'un temel veri yapılarını daha sistemli öğrenmek isteyenler Python özel ders programı kapsamında bu tür pratik senaryoları birebir uygulamalı olarak çalışabilir.
deepcopy Neden Daha Fazla Bellek ve İşlem Gerektirebilir?
deepcopy() fonksiyonu, bir nesneyi kopyalarken yalnızca en üst katmanı değil, içindeki her mutable alt nesneyi de tek tek yeniden oluşturur. Bu, fonksiyonun yapıyı rekürsif biçimde dolaşması anlamına gelir: liste içindeki liste, sözlük içindeki sözlük, hatta özel sınıf nesneleri bile ayrı ayrı ziyaret edilip yeniden inşa edilir. copy.copy() ise yalnızca dış nesneyi kopyalayıp içerideki referansları olduğu gibi bıraktığı için çok daha az işlem yapar.
Bu fark pratikte şu şekilde ortaya çıkar: deepcopy() her katmanda yeni nesneler oluşturduğu için hem daha fazla işlem adımı gerektirir hem de bellekte ek alan kaplar, çünkü artık orijinal veriyle hiçbir şey paylaşılmaz. copy.copy() ise sadece dış çerçeveyi kopyaladığından daha hafif kalır. Bu maliyet, birkaç elemanlı düz bir liste veya sözlük için pratikte fark edilmeyecek kadar küçüktür; ancak çok katmanlı, iç içe geçmiş büyük veri yapılarında (örneğin binlerce alt listeden oluşan bir yapı ya da derinlemesine iç içe sözlükler) bu maliyet daha belirgin hale gelir ve programın performansını gözle görülür biçimde etkileyebilir.
Bu yüzden deepcopy() kullanmadan önce kendine şu soruyu sormak gerekir: veri yapısı gerçekten çok katmanlı mı, yoksa tek seviyeli mi? Eğer yapı düzse ve içinde başka mutable nesne barındırmıyorsa, copy.copy() veya hatta dilim (slice) alma gibi daha basit yöntemler yeterli olur. Kendi bilgini test etmek ve bu tür temel-ileri seviye ayrımlarını netleştirmek istersen Python bilgi testi üzerinden hızlıca kontrol edebilirsin. Önemli olan, her durumda otomatik olarak "en güvenlisi deepcopy" mantığıyla hareket etmek yerine, veri yapısının derinliğine göre bilinçli bir seçim yapmaktır.
Hangi Durumda Hangi Kopyalama Yöntemi Tercih Edilmeli?
Doğru kopyalama yöntemini seçmek, veri yapısının içeriğine bakmakla başlar. Sorulması gereken tek soru şudur: bu nesnenin içinde başka mutable nesneler var mı, yoksa yalnızca sabit (immutable) değerler mi tutuluyor? Bu sorunun cevabı, shallow copy, deepcopy veya sade = ataması arasından hangisinin yeterli olduğunu doğrudan belirler. Gereğinden fazla önlem almak kodu yavaşlatır, gereğinden az önlem almak ise az önce incelediğimiz nested liste hatası gibi sessiz veri bozulmalarına yol açar.
- Veri yapısı düzse (flat) ve içinde yalnızca sayı, string, tuple gibi immutable elemanlar varsa,
copy.copy()veyalist.copy()ile alınan shallow copy tamamen güvenlidir; alt eleman paylaşımı bir risk oluşturmaz çünkü immutable nesneler zaten değiştirilemez. - Veri yapısı nested list, nested dict veya bu ikisinin karışımı gibi iç içe mutable nesneler barındırıyorsa ve kopyanın orijinalden tamamen bağımsız davranması gerekiyorsa,
copy.deepcopy()kullanılmalıdır; aksi halde iç nesneler üzerinden yapılan değişiklikler orijinal veriye de yansır. - Amaç yeni bir veri kopyası oluşturmak değil, yalnızca aynı nesneye başka bir isimle erişmekse (örneğin fonksiyon içinde geçici bir kısayol tanımlamak), sade
=ataması yeterlidir; burada zaten kopyalama değil referans paylaşımı istenmektedir.
Bu ayrımı ezberlemek yerine kod yazarken refleks haline getirmek, ileride hata ayıklamaya harcanacak saatleri baştan engeller. Bu tür nesne kimliği ve referans mantığını uygulamalı örneklerle pekiştirmek isteyenler için birebir Python dersleri kapsamında bu konular gerçek kod senaryoları üzerinden adım adım ele alınabilir.
Sık Yapılan Diğer Kopyalama Hataları ve Pratik İpuçları
Shallow copy hatası yalnızca copy.copy() çağrısıyla sınırlı değildir. list[:] slice yöntemi ve list.copy() metodu da tıpkı copy.copy() gibi shallow copy üretir; üçü de yalnızca en dış katmanı kopyalar, iç içe geçmiş mutable elemanları hâlâ orijinal nesneyle paylaşır. Bu nedenle "slice ile aldım, güvenliyim" varsayımı nested veri söz konusu olduğunda yanıltıcıdır.
Benzer bir risk fonksiyon parametrelerinde de karşımıza çıkar. Python'da bir listeyi veya sözlüğü fonksiyona parametre olarak geçirdiğinizde, fonksiyon içinde yeni bir kopya oluşmaz; fonksiyon aynı nesneye referansla erişir. Fonksiyon içinde append(), update() gibi yerinde değişiklik yapan metotlar çağrılırsa, fonksiyon dışındaki orijinal nesne de değişir. Bu davranış hata değildir, Python'ın referans mantığının doğal sonucudur; ancak fark edilmezse "fonksiyon çağırdım, orijinal veri neden değişti" şaşkınlığına yol açar.
def listeye_ekle(veri):
veri.append("yeni")
return veri
orijinal = ["a", "b"]
sonuc = listeye_ekle(orijinal)
print(orijinal) # ['a', 'b', 'yeni'] -- orijinal de değişti
Pratik ipucu nettir: kod yazmaya başlamadan önce "bu nesne mutable mı, iç içe mi?" sorusunu alışkanlık haline getirmek, hem fonksiyon parametrelerinde hem de kopyalama işlemlerinde büyük bir hata sınıfını baştan önler. Liste, sözlük veya set gibi mutable bir nesneyle çalışırken bu soruyu sormak, hangi kopyalama yönteminin gerekli olduğuna karar vermeyi otomatikleştirir. Bu tarz nesne kimliği ve mutable/immutable ayrımı sorularıyla kendinizi denemek isterseniz Python bilgi testi üzerinden mevcut seviyenizi hızlıca ölçebilirsiniz.
Sık Sorulan Sorular
Python'da copy() ile deepcopy() arasındaki temel fark nedir?
copy() yalnızca en dış nesneyi kopyalar, iç içe geçmiş mutable elemanların referansını orijinal ile paylaşmaya devam eder. deepcopy() ise iç içe geçmiş tüm nesneleri özyinelemeli biçimde tek tek kopyalayarak orijinalden tamamen bağımsız yeni bir yapı oluşturur.
Sığ (shallow) kopyalanan bir liste değiştirildiğinde orijinal liste neden değişir?
Değişiklik, kopyanın içindeki iç içe geçmiş bir listeye veya sözlüğe yapıldığında ortaya çıkar. Çünkü shallow copy bu iç nesneyi yeniden oluşturmaz, sadece aynı nesneye işaret eden bir referans kopyalar; dolayısıyla iç nesne üzerinde yapılan değişiklik hem kopyada hem orijinalde görünür.
Nested olmayan basit bir listede shallow copy kullanmak güvenli midir?
Evet, listenin elemanları yalnızca sayı, string veya tuple gibi immutable değerlerden oluşuyorsa shallow copy tamamen güvenlidir. Risk yalnızca listenin içinde başka bir liste, sözlük veya set gibi mutable bir nesne bulunduğunda ortaya çıkar.
deepcopy() her durumda kullanılmalı mı, yoksa gereksiz mi olabilir?
Her durumda kullanılması gerekmez. Veri düz ve immutable elemanlardan oluşuyorsa deepcopy() gereksiz bir işlem yüküdür; asıl gerekliliği, iç içe geçmiş mutable veri yapılarının orijinalden bağımsız olması gerektiği durumlarda ortaya çıkar.
Sözlüklerde (dict) shallow copy nasıl davranır?
dict.copy() veya copy.copy() ile alınan bir sözlük kopyasında anahtar-değer çiftlerinin en dış katmanı kopyalanır. Ancak değerlerden biri liste veya sözlük gibi mutable bir nesneyse, bu iç nesne orijinal sözlükle hâlâ paylaşılır ve birinde yapılan değişiklik diğerine de yansır.
id() fonksiyonu bir kopyalama hatasını tespit etmek için nasıl kullanılır?
id() fonksiyonu bir nesnenin bellekteki kimliğini döndürür. İki değişkenin id() değerleri aynıysa aynı nesneye işaret ediyorlar demektir; shallow copy sonrası dış nesnenin id'si farklı olsa da içindeki nested elemanların id'lerinin orijinalle aynı çıkması, referans paylaşımının hâlâ sürdüğünü doğrudan gösterir.
copy.copy(), list.copy() ve slice [:] gerçekten aynı sonucu mu verir?
Evet, üçü de aynı davranışı sergiler ve shallow copy üretir. Sadece en dış katmanı kopyalarlar; nested mutable elemanlar söz konusu olduğunda hepsi aynı referans paylaşımı riskini taşır, bu yüzden hangisinin kullanıldığı nested veri açısından fark yaratmaz.
Shallow copy ve deepcopy arasındaki ayrımı netleştirmek, Python'da veri bütünlüğünü korumanın en temel adımlarından biridir. Nesne kimliği, referans paylaşımı ve mutable/immutable ayrımı gibi kavramları uygulamalı örneklerle pekiştirmek isteyenler video eğitim içerikleri üzerinden konuyu kendi hızlarında tekrar tekrar izleyerek çalışabilir.