ProgramlamaPython Geliştirici / Veri Mühendisi

Değiştirilebilir bir nesneyi (örneğin bir liste veya sözlük) Python'da bir işleve geçirirken ne olur? İşlevin içinde ve dışında beklenmeyen değişikliklerden nasıl kaçınılır?

Hintsage yapay zeka asistanı ile mülakatları geçin

Cevap.

Sorunun geçmişi:

Python'da parametre geçişi "nesne referansı ile çağırma" prensibini (bazen paylaşma ile çağırma olarak adlandırılır) uygular. Bu, işlev içindeki herhangi bir değişkenin, dışarıdan geçirilen argümanla aynı bellek nesnesine işaret etmeye başladığı anlamına gelir.

Sorun:

Eğer işlev, geçirilen değiştirilebilir nesneyi (örneğin bir liste veya sözlük) değiştirirse, değişiklikler işlevin dışında da görünür. Bu, özellikle işlevin giriş verilerini değiştirmeyeceği beklendiğinde, zor anlaşılır hatalara neden olabilir.

Çözüm:

Yan etkilerden kaçınmak için işlev içinde nesnenin bir kopyasını oluşturmak veya değiştirilemez veri yapıları kullanmak gerekir. Kopyalamak için standart yöntemler kullanılır (örneğin, listeler için list.copy(), sözlükler için dict.copy() veya copy.deepcopy()).

Kod örneği:

def append_one(xs): xs.append(1) return xs lst = [0] append_one(lst) print(lst) # [0, 1] # Değişikliklerden nasıl kaçınılır? Kopya yapmak: def safe_append_one(xs): ys = xs.copy() ys.append(1) return ys lst2 = [0] safe_append_one(lst2) print(lst2) # [0]

Önemli özellikler:

  • Değiştirilebilir bir nesne geçişi, hem işlev içinde hem de dışında durumunu değiştirme imkanı tanır.
  • Bunu önlemek için verilerin kopyalanması (shallow/deep copy) kullanılır.
  • Değiştirilemez nesneler bu tür değişikliklere karşı korunur.

Kapanış Soruları.

list.copy() ile bir listenin kopyasının tamamen bağımsız olduğuna güvenilir mi?

Hayır — .copy() yüzeysel bir kopya yaratır. Eğer içinde değiştirilebilir alt nesneler varsa, onlardaki değişiklikler orijinalde de görünür.

import copy lst = [[1, 2], [3, 4]] shallow = lst.copy() shallow[0][0] = 42 print(lst) # [[42, 2], [3, 4]] deep = copy.deepcopy(lst) deep[0][0] = 100 print(lst) # [[42, 2], [3, 4]]

Girişe dayalı yeni bir nesne döndürmek, orijinalde değişiklik olmaması garantisi midir?

Her zaman değil. Yeni nesne içinde orijinalin parçaları kullanılıyorsa (örneğin, iç içe geçmiş bir listeye referans), orijinal nesne değiştirilebilir.

def duplicate_list(xs): return xs * 2 lst = [[1], [2]] res = duplicate_list(lst) res[0][0] = 999 print(lst) # [[999], [2]]

Değiştirilebilir nesneler için varsayılan argüman değerleri, işlevin tekrar tekrar çağrılmasında sorunlara yol açabilir mi?

Evet — varsayılan değer yalnızca işlev tanımlandığında bir kez hesaplanır.

def add_item(item, container=[]): container.append(item) return container print(add_item(1)) # [1] print(add_item(2)) # [1, 2]

Sık Hatalar ve Anti-Desenler

  • İşlev içinde kullanıcıya haber vermeden geçirilen değiştirilebilir nesnenin değiştirilmesi.
  • İç içe veri yapıları için yüzeysel kopyalama kullanmak (mütasyona uğrayan iç nesnelerle hata).
  • İşlev argümanlarında varsayılan değerler olarak değiştirilebilir nesnelerin kullanımı.

Gerçek Hayattan Örnek

Olumsuz Durum

Konfigürasyon işleme kütüphanesinde varsayılan değer olarak bir liste kullanıldı ve bu farklı işlev çağrıları arasında elemanların birikmesine yol açtı. Davranış belirsizdi ve uzun süre ortaya çıkmadı.

Artılar:

Tekrar çağrılar için daha az kod, görünür bellek tasarrufu.

Eksiler:

Gizli davranış, hata ayıklamada zorluklar, uzun vadeli hatalar.

Olumlu Durum

Her çağrıda yeni bir nesnenin açıkça oluşturulması için None değerinin varsayılan olarak kullanılması.

Artılar:

Öngörülebilirlik, beklenmedik yan etkilerin olmaması, güvenilirlik.

Eksiler:

Dikkat gerektirir ve biraz daha fazla kod yazmayı gerektirir.