Python'ın `copy` Modülü

Python'ın `copy` Modülü

Bu makale, Python'ın copy modülünü açıklar.

Yüzeysel (shallow) ve derin (deep) kopyalar arasındaki farklara odaklanarak, nesne çoğaltmanın temel mekanizmalarından özel sınıflardaki uygulamalara kadar net bir açıklama ve pratik örnekler sunuyoruz.

YouTube Video

Python'ın copy Modülü

Python'un copy modülü, nesne çoğaltmayı (kopyalamayı) ele almak için standart modüldür. Amacımız, yüzeysel ve derin kopyalar arasındaki farkı anlamak ve özel nesneler için kopyalama davranışını kontrol edebilmektir.

Temeller: Yüzeysel kopya nedir?

Burada liste ve sözlük gibi değiştirilebilir (mutable) nesneler için yüzeysel kopyaların davranışını gösteriyoruz. Yüzeysel kopya yalnızca en üst düzeydeki nesneyi çoğaltır ve içindeki başvuruları (referansları) paylaşır.

 1# Example: shallow copy with lists and dicts
 2import copy
 3
 4# A nested list
 5original = [1, [2, 3], 4]
 6shallow = copy.copy(original)
 7
 8# Mutate nested element
 9shallow[1].append(99)
10
11print("original:", original)  # nested change visible in original
12print("shallow: ", shallow)

Bu kodda, üst düzey liste çoğaltılır, ancak içteki alt listeler referansla paylaşılır; bu nedenle shallow[1].append(99) işlemi original içinde de yansır. Bu, yüzeysel kopyanın tipik bir davranışıdır.

Derin kopya nedir?

Derin kopya, nesneyi ve içindeki tüm başvuruları (referansları) özyinelemeli olarak çoğaltarak bağımsız bir nesne üretir. Karmaşık, iç içe geçmiş yapıları güvenli bir şekilde çoğaltmak istediğinizde bunu kullanın.

 1# Example: deep copy with nested structures
 2import copy
 3
 4original = [1, [2, 3], {'a': [4, 5]}]
 5deep = copy.deepcopy(original)
 6
 7# Mutate nested element of the deep copy
 8deep[1].append(100)
 9deep[2]['a'].append(200)
10
11print("original:", original)  # original remains unchanged
12print("deep:    ", deep)

Bu örnekte, deep üzerindeki değişiklikler original'ı etkilemez. deepcopy, tüm nesne grafiğini kopyalayarak bağımsız bir kopya (replika) üretir.

Hangisini kullanacağınıza nasıl karar verilir

Hangi kopyanın kullanılacağı, nesnenin yapısına ve amacınıza göre belirlenmelidir. İçteki değiştirilebilir öğelerdeki değişikliklerin özgün (orijinal) nesneyi etkilemesini istemiyorsanız, deepcopy uygundur. Bunun nedeni, tüm nesneyi özyinelemeli olarak çoğaltıp tamamen bağımsız bir kopya oluşturmasıdır.

Öte yandan, yalnızca en üst düzey nesneyi çoğaltmak yeterliyse ve hız ile bellek verimliliğini önemsiyorsanız, copy (yüzeysel kopya) daha uygundur.

  • İçteki değiştirilebilir öğeleri değiştirdiğinizde özgünün değişmesini istemiyorsanız → deepcopy kullanın.
  • Yalnızca en üst düzeyi çoğaltmak yeterli ve performansı (hız/bellek) önceliklendirmek istiyorsanız → copy (yüzeysel kopya) kullanın.

Yerleşik ve değiştirilemez (immutable) nesnelerin ele alınması

Değiştirilemez nesnelerin (ör. int, str ve içeriği değiştirilemez demetler/tuple'lar) genellikle kopyalanmasına gerek yoktur. copy.copy, uygun olduğunda aynı nesneyi döndürebilir.

 1# Example: copying immutables
 2import copy
 3
 4a = 42
 5b = copy.copy(a)
 6print(a is b)  # True for small ints (implementation detail)
 7
 8s = "hello"
 9t = copy.deepcopy(s)
10print(s is t)  # True (strings are immutable)

Değiştirilemez nesneleri kopyalamanın verimlilik açısından az fayda sağlaması nedeniyle, aynı nesne yeniden kullanılabilir. Bu, uygulama tasarımında nadiren endişelenmeniz gereken bir konudur.

Özel sınıflar: __copy__ ve __deepcopy__ tanımlama

Varsayılan kopyalama sınıfınız için beklediğiniz gibi değilse kendi kopyalama mantığınızı sağlayabilirsiniz. __copy__ ve __deepcopy__ tanımlarsanız, copy.copy ve copy.deepcopy bunları kullanır.

 1# Example: customizing copy behavior
 2import copy
 3
 4class Node:
 5    def __init__(self, value, children=None):
 6        self.value = value
 7        self.children = children or []
 8
 9    def __repr__(self):
10        return f"Node({self.value!r}, children={self.children!r})"
11
12    def __copy__(self):
13        # Shallow copy: create new Node, but reuse children list reference
14        new = self.__class__(self.value, self.children)
15        return new
16
17    def __deepcopy__(self, memo):
18        # Deep copy: copy value and deepcopy each child
19        new_children = [copy.deepcopy(child, memo) for child in self.children]
20        new = self.__class__(copy.deepcopy(self.value, memo), new_children)
21        memo[id(self)] = new
22        return new
  • __copy__ ve __deepcopy__ yöntemlerini uygulayarak, sınıfa özgü kopyalama davranışını esnek biçimde kontrol edebilirsiniz. Örneğin, bazı özniteliklerin aynı nesneye atıfta bulunması (paylaşması) gerekirken, diğer özniteliklerin tamamen yeni nesneler olarak çoğaltılması gereken durumları ele alabilirsiniz.
  • __deepcopy__'ye iletilen memo bağımsız değişkeni, özyinelemeli kopyalama sırasında zaten işlenen nesneleri kaydeden ve döngüsel referansların neden olduğu sonsuz döngüleri önleyen bir sözlüktür.
 1# Build tree
 2root_node = Node('root', [Node('child1'), Node('child2')])
 3
 4shallow_copy = copy.copy(root_node)   # shallow copy
 5deep_copy = copy.deepcopy(root_node)  # deep copy
 6
 7# Modify children
 8# affects both root_node and shallow_copy
 9shallow_copy.children.append(Node('child_shallow'))
10# affects deep_copy only
11deep_copy.children.append(Node('child_deep'))
12
13# Print results
14print("root_node:", root_node)
15print("shallow_copy:", shallow_copy)
16print("deep_copy:", deep_copy)
  • Bu kodda, shallow_copy değişkeni 'sığ kopya' yoluyla oluşturulur; bu nedenle nesnenin yalnızca üst düzeyi çoğaltılır ve içeride başvurduğu nesneler (bu durumda children listesi) orijinal root_node ile paylaşılır. Sonuç olarak, shallow_copy'ye yeni bir düğüm eklediğinizde, paylaşılan children listesi güncellenir ve değişiklik root_node'un içeriğine de yansır.
  • Öte yandan, deep_copy değişkeni 'derin kopya' yoluyla oluşturulur; bu nedenle iç yapı özyinelemeli olarak çoğaltılır ve root_node'dan tamamen bağımsız bir ağaç üretilir. Bu nedenle, deep_copy'ye yeni bir düğüm ekleseniz bile, bu root_node'u etkilemez.

Döngüsel başvurular (özyinelemeli nesneler) ve memo'nun önemi

Karmaşık bir nesne grafiği kendine başvurduğunda (döngüsel başvurular), deepcopy, halihazırda kopyalanmış nesneleri izlemek ve sonsuz döngüleri önlemek için bir memo sözlüğü kullanır.

 1# Example: recursive list and deepcopy memo demonstration
 2import copy
 3
 4a = []
 5b = [a]
 6a.append(b)  # a -> [b], b -> [a]  (cycle)
 7
 8# deepcopy can handle cycles
 9deep = copy.deepcopy(a)
10print("deep copy succeeded, length:", len(deep))
  • Dahili olarak deepcopy, zaten çoğaltılmış nesnelere başvurmak için memo'yu kullanır; bu, döngüler var olduğunda bile güvenli kopyalamayı sağlar. Özyinelemeyi elle yaptığınızda benzer bir mekanizmaya ihtiyaç duyarsınız.

copyreg ve serileştirme tarzı özel kopyalama (ileri düzey)

Kitaplıklarla uyum içinde özel kopyalama davranışını kaydetmek istiyorsanız, nesnelerin nasıl (yeniden) oluşturulacağını kaydetmek için standart copyreg modülünü kullanabilirsiniz. Bu, karmaşık nesneler ve C uzantı türleri için yararlıdır.

 1# Example: using copyreg to customize pickling/copy behavior (brief example)
 2import copy
 3import copyreg
 4
 5class Wrapper:
 6    def __init__(self, data):
 7        self.data = data
 8
 9def reduce_wrapper(obj):
10    # Return callable and args so object can be reconstructed
11    return (Wrapper, (obj.data,))
12
13copyreg.pickle(Wrapper, reduce_wrapper)
14
15w = Wrapper([1, 2, 3])
16w_copy = copy.deepcopy(w)
17print("w_copy.data:", w_copy.data)
  • copyreg kullanarak hem pickle hem de deepcopyyi etkileyen yeniden oluşturma kuralları sağlayabilirsiniz. Bu, ileri düzey bir API'dir ve çoğu durumda __deepcopy__ yeterlidir.

Pratik uyarılar ve tuzaklar

copy modülünü kullanırken doğru davranışı sağlamak için dikkat edilmesi gereken birkaç önemli nokta vardır. Aşağıda, geliştirme sırasında karşılaşabileceğiniz yaygın tuzakları ve bunlardan nasıl kaçınacağınızı açıklıyoruz.

  • Performans deepcopy, önemli miktarda bellek ve CPU tüketebilir; bu yüzden gerçekten gerekli olup olmadığını dikkatlice değerlendirin.
  • Paylaşmak istediğiniz öğelerin ele alınması Büyük önbellekler gibi bazı özniteliklerin paylaşımlı kalmasını istiyorsanız, __deepcopy__ içinde onların referanslarını kopyalamaktan özellikle kaçının.
  • Değiştirilemez iç durum İçeride değiştirilemez veri tutuyorsanız, kopyalama gereksiz olabilir.
  • İş parçacıkları ve harici kaynaklar Soketler ve dosya tanıtıcıları gibi kopyalanamayan kaynakları kopyalamak ya anlamsızdır ya da hatalara yol açar; bu nedenle tasarım itibarıyla bunları kopyalamaktan kaçınmalısınız.

Pratik örnek: sözlükleri güvenle güncellemek için bir kalıp

Karmaşık bir yapılandırma nesnesini güncellerken, bu örnek özgün ayarları korumak için deepcopy kullanır.

 1# Example: safely update a nested configuration using deepcopy
 2import copy
 3
 4default_config = {
 5    "db": {"host": "localhost", "ports": [5432]},
 6    "features": {"use_cache": True, "cache_sizes": [128, 256]},
 7}
 8
 9# Create a working copy to modify without touching defaults
10working = copy.deepcopy(default_config)
11working["db"]["ports"].append(5433)
12working["features"]["cache_sizes"][0] = 512
13
14print("default_config:", default_config)
15print("working:", working)

deepcopy kullanarak, varsayılan ayarları bozma riski olmadan türetilmiş yapılandırmalar oluşturabilirsiniz. Bu, özellikle yapılandırmalar gibi iç içe geçmiş değiştirilebilir yapılarda faydalıdır.

En iyi uygulamalar

copy modülünü güvenli ve etkili bir şekilde kullanmak için aşağıdaki pratik yönergeleri akılda tutmak önemlidir.

  • Değişikliklerin olup olmayacağına ve kapsamına göre yüzeysel ve derin kopya arasında seçim yapın.
  • Beklenen davranışı elde etmek için özel sınıflarda __copy__ ve __deepcopy__ uygulayın.
  • Derin kopyalar maliyetli olduğundan, mümkün olduğunda tasarım yoluyla kopyalama ihtiyacını azaltın. Diğer tekniklerin yanı sıra değiştirilemezliği ve açık klon yöntemlerini değerlendirin.
  • Döngüsel başvurularla uğraşırken ya deepcopy'den yararlanın ya da elle memo benzeri bir mekanizma sağlayın.
  • Dosya tanıtıcıları ve iş parçacıkları gibi harici kaynakların kopyalanmamasını sağlayacak şekilde tasarlayın.

Sonuç

copy modülü, Python'da nesne çoğaltma için standart araçtır. Yüzeysel ve derin kopyalar arasındaki farkları doğru biçimde anlayıp gerektiğinde özel kopyalama davranışını uygulayarak çoğaltmayı güvenli ve öngörülebilir şekilde gerçekleştirebilirsiniz. Tasarım aşamasında kopyalamanın gerçekten gerekli olup olmadığını ve nelerin paylaşılması gerektiğini netleştirerek gereksiz hatalardan ve performans sorunlarından kaçınabilirsiniz.

Yukarıdaki makaleyi, YouTube kanalımızda Visual Studio Code'u kullanarak takip edebilirsiniz. Lütfen YouTube kanalını da kontrol edin.

YouTube Video