Python: Assignment vs Shallow Copy vs Deep Copy

Kader Miyanyedi
4 min readJul 30, 2023

--

Herkese selamlar! Python’da bir nesneyi kopyalamak için 3 farklı yöntem karşımıza çıkar. Bu yazıda bu yöntemlerin farklarını ve bellekte nasıl yer edindiklerini & çalıştıklarını inceleyeceğiz.

Öncelikle Python’da her şey bir nesnedir. Yani bir integer değişken tanımladığımız zamanda, bir fonksiyon tanımladığımız zamanda bu bir nesnedir. Temel olarak bir nesne tanımlandığı zaman bellekte bir yer ayrılır ve değişkenler bu bellekteki adresi tutan referanslardır. Python^da yer alan built-in id() methodu ile nesnenin bellekte yer aldığı adresi öğrenebiliriz.

>> x = 1
>> id(x)
4301439216

Nesneler farklı bir nesnenin referansını tutabilirler. Örneğin bir list nesnesi içerisinde farklı nesnelerin referanslarını tutan bir veri yapısı olarak karşımıza çıkar.

>>> x = [1, 2, 3]

>>> print(f"{x=} {id(x)=}")
x=[1, 2, 3] id(x)=4309066624

>>> print(f"{x[0]=} {id(x[0])=}")
x[0]=1 id(x[0])=4301439216

Peki bir nesneyi kopyalamak için hangi yöntemleri kullanabiliriz ?

✨ Variable Assignment

Dökümanda da belirtildiği gibi değişken atamasını bir kopyalama işlemi olarak göremeyiz. Yine de değinmek istediğim bir konu olduğu için bu yazıda yer verdim. Bir değişkeni = operator ile farklı bir değişkene atadığımız zaman bellekte yeni bir nesne oluşmaz. Yeni tanımladığımız değişken bellekteki aynı adresin referansını tutmaya başlar.

>>> x = [1, 2, 3]
>>> y = x

>>> print(f"{x=} {id(x)=}")
x=[1, 2, 3] id(x)=4309278272

>>> print(f"{y=} {id(y)=}")
y=[1, 2, 3] id(y)=4309278272

Tanımlamış olduğumuz x ve y değişkenleri bellekte aynı nesnenin adresini referans etmektedirler. Bu durumda, herhangi bir değişiklik yapıldığında her iki değişkenin de etkilenmesi gerekmektedir.

>>> x = [1, 2, 3]
>>> y = x

>>> print(f"{x=} {y=}")
x=[1, 2, 3] y=[1, 2, 3]

>>> x.append(4)

>>> print(f"{x=} {y=}")
x=[1, 2, 3, 4] y=[1, 2, 3, 4]

✨ Shallow Copy

Bir nesneyi, Python built-in copy kütüphanesinde yer alan copy() methodunu kullanarak veya nesnenin copy() methodunu kullanarak kopyaladığımızda bellekte yeni bir nesne oluşur ve orijinal nesnenin referansları ile doldurulur. Bu methodlar ile kopyalama işlemine shallow copy ismi verilir.

>>> x = [1, 2, 3]
>>> z = x.copy()

>>> print(f"{x=} {id(x)=}")
x=[1, 2, 3] id(x)=4309263040

>>> print(f"{z=} {id(z)=}")
z=[1, 2, 3] id(z)=4308960320

>>> print(f"{x[0]=} {id(x[0])=}")
x[0]=1 id(x[0])=4301439216

>>> print(f"{z[0]=} {id(z[0])=}")
z[0]=1 id(z[0])=4301439216
>>> x = [1, 2, 3]
>>> z = x.copy()
>>> y = x

>>> x.append(4)
>>> x.append(5)

>>> print(f"{x=} {id(x)=}")
x=[1, 2, 3, 4, 5] id(x)=4309263040

>>> print(f"{z=} {id(z)=}")
z=[1, 2, 3] id(z)=4309258496

>>> print(f"{y=} {id(y)=}")
y=[1, 2, 3, 4, 5, 5] id(y)=4309279616

Listeye yeni elemanlar eklediğimizde y değişkeni bu durumdan etkilenirken z değişkeni etkilenmemiştir. Fakat z ve x listelerinin ilk elemanlarının aynı bellek adresinin referansını tuttuğunu unutmayalım.

Mevcut örneğimizde x[0] değerini değiştirmemiz durumunda z listesi bu durumdan yine etkilenmeyecektir. Çünkü x ve z listelerinin ilk elemanları birer integer nesnesidir ve Python’da integer nesneleri immutable(değiştirilemez) nesnelerdir.

✏️✏️ Note: Python’da nesneler mutable(değiştirilebilir) ve immutable(değiştirilemez) olarak sınıflandırabiliriz. Immutable nesnelerin değerleri değiştirilemez. Bu nesneyi değiştirdiğimiz zaman aslında bellekte yeni bir adres tanımlaması yapılır ve değişken yeni bellek adresini referans etmeye başlar. Mutable nesnelerde bellek adresini değiştirmeden nesnenin değerini değiştirebiliriz.
Immutable nesneler; integer, string, tuples…
Mutable nesneler; list, dictionary, set…

>>> x = [1, 2, 3]
>>> z = x.copy()
>>> x[0] = 0

>>> print(f"{x[0]=} {id(x[0])=}")
x[0]=0 id(x[0])=4301439184

>>> print(f"{z[0]=} {id(z[0])=}")
z[0]=1 id(z[0])=4301439216

Liste içerisinde liste tutan yeni bir değişken tanımlayalım ve güncellediğimizde neler olacağını inceleyelim:

>>> a = [[1,2], [2,3], 4]
>>> b = a.copy()

>>> a.append(8)
>>> a
[[1, 2], [2, 3], 4, 8]
>>> b
[[1, 2], [2, 3], 4]

>>> a[0][0] = 0
>>> a
[[0, 2], [2, 3], 4, 8]
>>> b
[[0, 2], [2, 3], 4]

Kopyalanan nesne, orijinal nesne ile aynı bellek adresini referans ettiği için yapılan değişiklikten her iki nesne de etkilenmiştir.

✨ Deep Copy

Son olarak Python built-in copy kütüphanesi içerisinde yer alan deepcopy() methodunu inceleyeceğiz. deepcopy() methodu ile orijinal nesnenin kopyası oluşturulur. Orijinal nesnenin tuttuğu diğer nesneleri de recursive(yinelemeli) olarak kopyalar ve nesneyi doldurur. Bu durumda, shallow copy aksine orijinal nesnedeki herhangi bir değişiklik kopyalanan nesneyi etkilemez.

>>> from copy import deepcopy

>>> a = [[1,2], [2,3]]
>>> b = deepcopy(a)

>>> id(a[0])
4553398912

>>> id(b[0])
4540246720

>>> id(a[0][0])
4302553328

>>> id(b[0][0])
4302553328
>>> a[0][0] = 0
>>> a
[[0, 2], [2, 3]]
>>> b
[[1, 2], [2, 3]]

>>> id(a[0][0])
4324858480
>>> id(b[0][0])
4324858512

📚 Kaynaklar

[1] https://docs.python.org/3/library/copy.html#module-copy
[2] assign vs. shallow vs. deep copy (beginner — intermediate) - anthony explains

--

--