Listelerin Toplanması

15-2.25.20

İlginç gelebilir, Python’da iki listeyi + operatörü ile toplanabilir. Fakat çıkartılamaz, çarpılamaz ya da bölünemez.

İki listeyi topladığımız zaman yeni bir liste elde ederiz. Bu yeni listenin elemanları + operatörünün sol operandındaki liste elemanlarına sağındaki liste elemanlarının uc uca eklenmesi ile oluşur yani bir concat işlemi vardır. Yeni listenin len() uzunluğu bu durumda len(sol) + len(sağ) olmaktadır.

Örneğin:

>>> x = [1, 2, 3]
>>> y = [10, 20, 30]

>>> x + y
[1, 2, 3, 10, 20, 30]

Burada bir çeşit sığ kopyalama yani shallow copy işlemi yapılır. Yeni listedeki elemanlar aslında orijinal listedekiler ile aynı nesneleri gösterir. Yani:

>>> x = [1, 2, 3]
>>> y = [10, 20, 30]

>>> z = x + y

>>> z[0] is x[0]
True

>>> z[3] is y[0]
True

Bir örnek daha:

>>> a = [1, 2, 3]
>>> b = [[10, 20, 30]]

>>> a + b # Liste olarak eklendi, dikkat!
[1, 2, 3, [10, 20, 30]]

İki liste toplanabildiği gibi üç ya da daha fazla da toplama yapılabilir. + işlemi soldan sağa öncelikliği olduğu için a + b + c dersek önce a + b yapılır sonra bu liste ile c toplanır.

16-0.14.15

../_images/list-toplama-bellek.png

İki liste toplandığında oluşan yeni liste aslında toplanan listelerin gösterdiği nesneleri gösterir, shallow copy mantığına benzer buradaki yaklaşım vardır.

Bu durumda listeler değiştirilemez bir nesne türü olan int nesnesini göstermektedir. Biz bir listeyi güncellediğimizde, örneğin x[0] = 5 diyerek, değeri 5 olan yeni bir int nesnesi oluşur ve x[0] artık bu nesneyi gösterir ama z[0] hala eski nesneyi göstermektedir.

>>> x = [1, 2]
>>> y = [10, 20]

>>> z = x + y

>>> x[0] is z[0]
True

>>> x[0] = 5

>>> x
[5, 2]

>>> z
[1, 2, 10, 20]

>>> x[0] is z[0]
False

Aşağıdaki gibi düşünebiliriz:

../_images/list-toplama-bellek-immutable.png

Tüm int nesneleri hala “kullanıldığı” için garbage collector gibi bir mekanizma ile “çöpe atılmazlar.”

Fakat iç içe listeler gibi mutable nesnelerin yer almaya başladığı durumlarda işler değişmeye başlayacaktır. Aşağıdaki kodu ele alalım:

>>> x = [1, 2, [3, 4]]
>>> y = [5, 6]
>>> z = x + y

>>> x[2][0] is z [2][0]
True

Gösterimine bakalım:

../_images/list-toplama-bellek-icice.png

Durum C’deki double pointer dereference durumlarına benzemeye başladı 🤔

Şimdi içerideki listede güncelleme yapalım ve duruma bakalım.

>>> z[2][0] = 10

>>> x[2][0] is z [2][0]
True

>>> x
[1, 2, [10, 4]]

Bu durumda biz z yi güncelledik ama x de değişti. Bellekteki görünüm aşağıdaki gibi olmaktadır.

../_images/list-toplama-bellek-icice-update.png

Artık bir referansı kalmayan 3 değerli int nesnesi bir noktada otomatik olarak çöpe atılacaktır.

16-0.30.00

Not

Ek bilgi

Eğer deep copy yapmak istiyorsak copy modülü içerisindeki copy.deepcopy() fonksiyonunu kullanabiliriz.

Boş Listelerin Toplanması

Toplama sırasında iki listenin biri ya da her ikisi de boş liste olsa bile toplama işlemi sonucunda yeni bir liste yaratılmaktadır. Programlama dünyasında observable side effect yani gözlemlenebilir yan etki kavramı vardır. Böyle bir durumda Python implementasyonu bir optimizasyon yapabilir.

Aşağıdaki durum geçerlidir:

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

Bir listeyi doğrudan toplayarak oluşturursak bu optimizasyon kapsamında Python implementasyonu doğrudan birleştirilmiş listeyi tek seferde yapabilir, yani iki listeyi ayrı ayrı oluşturup toplamayabilir.

x = [1, 2, 3] + [4, 5] # doğrudan [1, 2, 3, 4, 5] yaratılabilir

+== +

Python’da ve birçok programlama dilinde a += b işleminin a = a + b ile eşdeğer olduğunu söylüyoruz. Fakat Python’da bu denklik listeler için geçerli DEĞİLDİR.

16-1.10.45

a = a + b dediğimizde aslında yeni bir liste oluşuyor ve a artık yeni bir listeyi göstermeye başlıyor. Örneğin:

>>> a = [1, 2, 3]
>>> b = [4, 5]

>>> id(a)
133419689172608

>>> a = a + b

>>> a
[1, 2, 3, 4, 5]

>>> id(a)   # ilk id(a) ile aynı değil
133419689173952

a += b nin anlamı ise listelerde farklıdır. b listesinin elemanlarının mevcut a listesinin sonuna eklenmesi anlamı çıkmaktadır. Yani aslında a.extend(b) gibi bir etki yaratmaktadır. Örneğin:

>>> a = [1, 2, 3]
>>> b = [4, 5]

>>> id(a)
133419689170176

>>> a += b

>>> a
[1, 2, 3, 4, 5]

>>> id(a)  # a'nın id değeri değişmedi
133419689170176

Yani konu listeler olunca a = a + b ile a += b aynı anlamda değildir.

a.extend(b)

Python dokümanlarına göre [1] a bir list türden olmak üzere a.extend(b) ile a += b tamamen aynı anlamdadır. extend() metodun parametresinin herhangi bir dolaşılabilir nesne olabileceğini söylemiştik. Bu durum a += b için de geçerlidir.

>>> a = [1, 2]

>>> a
[1, 2]

>>> a += 'alper'

>>> a
[1, 2, 'a', 'l', 'p', 'e', 'r']

>>> a.extend('yazar')

>>> a
[1, 2, 'a', 'l', 'p', 'e', 'r', 'y', 'a', 'z', 'a', 'r']

16-1.27.30