11. Standart Kütüphanenin Kısa Özeti --- Bölüm II
*************************************************

Bu ikinci özet, profesyonel programlama ihtiyaçlarını destekleyen daha
gelişmiş modülleri kapsar. Bu modüller nadiren küçük komut
dosyalarında bulunur.


11.1. Çıktı Biçimlendirmesi
===========================

"reprlib" modülü, büyük veya derinlemesine iç içe kapların kısaltılmış
gösterimleri için özelleştirilmiş bir "repr()" sürümünü sağlar:

   >>> import reprlib
   >>> reprlib.repr(set('supercalifragilisticexpialidocious'))
   "{'a', 'c', 'd', 'e', 'f', 'g', ...}"

"pprint" modülü, hem yerleşik hem de kullanıcı tanımlı nesnelerin
yorumlayıcı tarafından okunabilecek şekilde yazdırılması üzerinde daha
karmaşık kontrol sunar. Sonuç bir satırdan uzun olduğunda, "pretty
printer" veri yapısını daha net bir şekilde ortaya çıkarmak için satır
sonları ve girintiler ekler:

   >>> import pprint
   >>> t = [[[['black', 'cyan'], 'white', ['green', 'red']], [['magenta',
   ...     'yellow'], 'blue']]]
   ...
   >>> pprint.pprint(t, width=30)
   [[[['black', 'cyan'],
      'white',
      ['green', 'red']],
     [['magenta', 'yellow'],
      'blue']]]

"textwrap" modülü, metin paragraflarını belirli bir ekran genişliğine
uyacak şekilde biçimlendirir:

   >>> import textwrap
   >>> doc = """The wrap() method is just like fill() except that it returns
   ... a list of strings instead of one big string with newlines to separate
   ... the wrapped lines."""
   ...
   >>> print(textwrap.fill(doc, width=40))
   The wrap() method is just like fill()
   except that it returns a list of strings
   instead of one big string with newlines
   to separate the wrapped lines.

"locale" modülü kültüre özgü veri biçimlerinden oluşan bir
veritabanına erişmektedir. Yerel ortamın biçim işlevinin gruplandırma
özniteliği, sayıları grup ayırıcılarıyla biçimlendirmek için doğrudan
bir yol sağlar:

   >>> import locale
   >>> locale.setlocale(locale.LC_ALL, 'English_United States.1252')
   'English_United States.1252'
   >>> conv = locale.localeconv()          # get a mapping of conventions
   >>> x = 1234567.8
   >>> locale.format("%d", x, grouping=True)
   '1,234,567'
   >>> locale.format_string("%s%.*f", (conv['currency_symbol'],
   ...                      conv['frac_digits'], x), grouping=True)
   '$1,234,567.80'


11.2. Şablonlamak
=================

"string" modülü  birçok yönlü kullanıcılar tarafından düzenlemeye
uygun basitleştirilmiş sözdizimine sahip şablon sınıfı "Template"
içerir.   Bu, kullanıcıların uygulamayı değiştirmek zorunda kalmadan
uygulamalarını özelleştirmelerini sağlar.

Format, geçerli Python tanımlayıcılarıyla (alfasayısal karakterler ve
alt çizgiler) "$" tarafından oluşturulan yer tutucu adlarını kullanır.
Yer tutucuyu ayraçla çevrelemek, onu araya giren boşluklar olmadan
daha alfasayısal harflerle takip etmenizi sağlar.  "$$" yazmak tek bir
"$" oluşturur:

   >>> from string import Template
   >>> t = Template('${village}folk send $$10 to $cause.')
   >>> t.substitute(village='Nottingham', cause='the ditch fund')
   'Nottinghamfolk send $10 to the ditch fund.'

"substitute()" yöntemi, bir sözlükte veya anahtar sözcük bağımsız
değişkeninde yer tutucu sağlandığında "KeyError" öğesini yükseltir.
Adres mektup birleştirme stili uygulamalar için, kullanıcı tarafından
sağlanan veriler eksik olabilir ve "safe_substitute()" yöntemi daha
uygun olabilir --- veriler eksikse yer tutucuları değiştirmez:

   >>> t = Template('Return the $item to $owner.')
   >>> d = dict(item='unladen swallow')
   >>> t.substitute(d)
   Traceback (most recent call last):
     ...
   KeyError: 'owner'
   >>> t.safe_substitute(d)
   'Return the unladen swallow to $owner.'

Şablon alt sınıfları özel bir sınırlayıcı belirtebilir.  Örneğin, bir
fotoğraf tarayıcısı için toplu yeniden adlandırma yardımcı programı,
geçerli tarih, görüntü sıra numarası veya dosya biçimi gibi yer
tutucular için yüzde işaretlerini kullanmayı seçebilir:

   >>> import time, os.path
   >>> photofiles = ['img_1074.jpg', 'img_1076.jpg', 'img_1077.jpg']
   >>> class BatchRename(Template):
   ...     delimiter = '%'
   ...
   >>> fmt = input('Enter rename style (%d-date %n-seqnum %f-format):  ')
   Enter rename style (%d-date %n-seqnum %f-format):  Ashley_%n%f

   >>> t = BatchRename(fmt)
   >>> date = time.strftime('%d%b%y')
   >>> for i, filename in enumerate(photofiles):
   ...     base, ext = os.path.splitext(filename)
   ...     newname = t.substitute(d=date, n=i, f=ext)
   ...     print('{0} --> {1}'.format(filename, newname))

   img_1074.jpg --> Ashley_0.jpg
   img_1076.jpg --> Ashley_1.jpg
   img_1077.jpg --> Ashley_2.jpg

Templating için başka bir uygulama, program mantığını birden çok çıktı
biçiminin ayrıntılarından ayırmaktır.  Bu, XML dosyaları, düz metin
raporları ve HTML web raporları için özel şablonların değiştirilmesini
mümkün kılar.


11.3. İkili Veri Kaydı Düzenleriyle Çalışma
===========================================

"struct" modülü, değişken uzunluklu ikili kayıt formatlarıyla çalışmak
için "pack()" ve "unpack()" işlevlerini sağlar. Aşağıdaki örnek,
"zipfile" modülünü kullanmadan bir ZIP dosyasındaki başlık
bilgilerinin nasıl döngüye alınacağını gösterir. Paket kodları ""H""
ve ""I"" sırasıyla iki ve dört baytlık işaretsiz sayıları temsil eder.
""<"", standart boyutta ve küçük endian bayt düzeninde olduklarını
gösterir:

   import struct

   with open('myfile.zip', 'rb') as f:
       data = f.read()

   start = 0
   for i in range(3):                      # show the first 3 file headers
       start += 14
       fields = struct.unpack('<IIIHH', data[start:start+16])
       crc32, comp_size, uncomp_size, filenamesize, extra_size = fields

       start += 16
       filename = data[start:start+filenamesize]
       start += filenamesize
       extra = data[start:start+extra_size]
       print(filename, hex(crc32), comp_size, uncomp_size)

       start += extra_size + comp_size     # skip to the next header


11.4. Çoklu iş parçacığı
========================

Diş açma, sıralı olarak bağımlı olmayan görevlerin ayrıştırılması için
bir tekniktir. Diğer görevler arka planda çalışırken kullanıcı
girdisini kabul eden uygulamaların yanıt verme hızını artırmak için iş
parçacıkları kullanılabilir. İlgili bir kullanım durumu, başka bir iş
parçacığındaki hesaplamalara paralel olarak I/O çalıştırmaktadır.

Aşağıdaki kod, ana program çalışmaya devam ederken üst düzey
"threading" modülünün görevleri arka planda nasıl çalıştırabileceğini
gösterir:

   import threading, zipfile

   class AsyncZip(threading.Thread):
       def __init__(self, infile, outfile):
           threading.Thread.__init__(self)
           self.infile = infile
           self.outfile = outfile

       def run(self):
           f = zipfile.ZipFile(self.outfile, 'w', zipfile.ZIP_DEFLATED)
           f.write(self.infile)
           f.close()
           print('Finished background zip of:', self.infile)

   background = AsyncZip('mydata.txt', 'myarchive.zip')
   background.start()
   print('The main program continues to run in foreground.')

   background.join()    # Wait for the background task to finish
   print('Main program waited until background was done.')

Çok iş parçacıklı uygulamaların temel zorluğu, verileri veya diğer
kaynakları paylaşan iş parçacıklarını koordine etmektir. Bu amaçla, iş
parçacığı modülü, kilitler, olaylar, koşul değişkenleri ve semaforlar
dahil olmak üzere bir dizi senkronizasyon ilkesi sağlar.

Bu araçlar güçlü olsa da, küçük tasarım hataları, yeniden üretilmesi
zor sorunlara neden olabilir. Bu nedenle, görev koordinasyonuna
yönelik tercih edilen yaklaşım, bir kaynağa tüm erişimi tek bir iş
parçacığında yoğunlaştırmak ve ardından bu iş parçacığını diğer iş
parçacıklarından gelen isteklerle beslemek için "queue" modülünü
kullanmaktır. İş parçacıkları arası iletişim ve koordinasyon için
"Queue" nesnelerini kullanan uygulamaların tasarımı daha kolay, daha
okunaklı ve daha güvenilirdir.


11.5. Günlükleme
================

"logging" modülü, tam özellikli ve esnek bir kayıt sistemi sunar. En
basit haliyle, günlük mesajları bir dosyaya veya "sys.stderr" adresine
gönderilir:

   import logging
   logging.debug('Debugging information')
   logging.info('Informational message')
   logging.warning('Warning:config file %s not found', 'server.conf')
   logging.error('Error occurred')
   logging.critical('Critical error -- shutting down')

Bu, aşağıdaki çıktıyı üretir:

   WARNING:root:Warning:config file server.conf not found
   ERROR:root:Error occurred
   CRITICAL:root:Critical error -- shutting down

Varsayılan olarak, bilgi ve hata ayıklama mesajları bastırılır ve
çıktı standart hataya gönderilir. Diğer çıktı seçenekleri, mesajları
e-posta, datagramlar, yuvalar veya bir HTTP Sunucusuna yönlendirmeyi
içerir. Yeni filtreler mesaj önceliğine göre farklı yönlendirme
seçebilir: "DEBUG", "INFO", "WARNING", "ERROR" , ve "CRITICAL".

Günlük kaydı sistemi, doğrudan Python'dan yapılandırılabilir veya
uygulamayı değiştirmeden özelleştirilmiş günlük kaydı için kullanıcı
tarafından düzenlenebilir bir yapılandırma dosyasından yüklenebilir.


11.6. Zayıf Başvurular
======================

Python otomatik bellek yönetimi yapar (çoğu nesne için referans sayımı
ve döngüleri ortadan kaldırmak için *garbage collection*). Hafıza, ona
yapılan son başvurunun ortadan kaldırılmasından kısa bir süre sonra
serbest bırakılır.

Bu yaklaşım çoğu uygulama için iyi sonuç verir ancak bazen nesneleri
yalnızca başka bir şey tarafından kullanıldıkları sürece izlemeye
ihtiyaç duyulur. Ne yazık ki, sadece onları izlemek onları kalıcı
kılan bir referans oluşturur. "weakref" modülü, referans oluşturmadan
nesneleri izlemek için araçlar sağlar. Nesneye artık ihtiyaç
duyulmadığında, zayıf referans tablosundan otomatik olarak kaldırılır
ve zayıf referans nesneleri için bir geri arama tetiklenir. Tipik
uygulamalar, oluşturması pahalı olan nesneleri önbelleğe almayı
içerir:

   >>> import weakref, gc
   >>> class A:
   ...     def __init__(self, value):
   ...         self.value = value
   ...     def __repr__(self):
   ...         return str(self.value)
   ...
   >>> a = A(10)                   # create a reference
   >>> d = weakref.WeakValueDictionary()
   >>> d['primary'] = a            # does not create a reference
   >>> d['primary']                # fetch the object if it is still alive
   10
   >>> del a                       # remove the one reference
   >>> gc.collect()                # run garbage collection right away
   0
   >>> d['primary']                # entry was automatically removed
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
       d['primary']                # entry was automatically removed
     File "C:/python311/lib/weakref.py", line 46, in __getitem__
       o = self.data[key]()
   KeyError: 'primary'


11.7. Listelerle Çalışma Araçları
=================================

Birçok veri yapısı ihtiyacı yerleşik liste türüyle karşılanabilir.
Ancak bazen farklı performans ödünleşimleri ile alternatif
uygulamalara ihtiyaç duyulmaktadır.

"array" modülü, yalnızca homojen verileri depolayan ve daha kompakt
bir şekilde depolayan bir liste gibi bir "array()" nesnesi sağlar.
Aşağıdaki örnek, normal Python int nesneleri listeleri için giriş
başına olağan 16 bayt yerine iki baytlık işaretsiz ikili sayılar (tür
kodu ""H"") olarak saklanan bir sayı dizisini gösterir:

   >>> from array import array
   >>> a = array('H', [4000, 10, 700, 22222])
   >>> sum(a)
   26932
   >>> a[1:3]
   array('H', [10, 700])

"collections" modülü, eklemelerin daha hızlı olduğu ve sol taraftan
açılan ancak ortada daha yavaş aramaların olduğu bir liste gibi bir
"deque()" nesnesi sağlar. Bu nesneler, kuyruklar uygulamak ve ilk ağaç
aramalarını genişletmek için çok uygundur:

   >>> from collections import deque
   >>> d = deque(["task1", "task2", "task3"])
   >>> d.append("task4")
   >>> print("Handling", d.popleft())
   Handling task1

   unsearched = deque([starting_node])
   def breadth_first_search(unsearched):
       node = unsearched.popleft()
       for m in gen_moves(node):
           if is_goal(m):
               return m
           unsearched.append(m)

Alternatif liste uygulamalarına ek olarak, kütüphane ayrıca sıralanmış
listeleri işlemek için işlevlere sahip "bisect" modülü gibi başka
araçlar da sunar:

   >>> import bisect
   >>> scores = [(100, 'perl'), (200, 'tcl'), (400, 'lua'), (500, 'python')]
   >>> bisect.insort(scores, (300, 'ruby'))
   >>> scores
   [(100, 'perl'), (200, 'tcl'), (300, 'ruby'), (400, 'lua'), (500, 'python')]

"heapq" modülü, düzenli listelere dayalı yığınları uygulamak için
işlevler sağlar. En düşük değerli giriş her zaman sıfır konumunda
tutulur. Bu, en küçük öğeye tekrar tekrar erişen ancak tam liste
sıralamasını çalıştırmak istemeyen uygulamalar için kullanışlıdır:

   >>> from heapq import heapify, heappop, heappush
   >>> data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
   >>> heapify(data)                      # rearrange the list into heap order
   >>> heappush(data, -5)                 # add a new entry
   >>> [heappop(data) for i in range(3)]  # fetch the three smallest entries
   [-5, 0, 1]


11.8. Ondalık Kayan Nokta Aritmetiği
====================================

"decimal" modülü, ondalık kayan nokta aritmetiği için bir "Decimal"
veri tipi sunar. İkili kayan noktanın yerleşik "float" uygulamasıyla
karşılaştırıldığında, sınıf özellikle için yararlıdır

* tam ondalık gösterim gerektiren finansal uygulamalar ve diğer
  kullanımlar,

* hassasiyet üzerinde kontrol,

* yasal veya düzenleyici gereklilikleri karşılamak için yuvarlama
  üzerinde kontrol,

* önemli ondalık basamakların izlenmesi veya

* kullanıcının sonuçların elle yapılan hesaplamalarla eşleşmesini
  beklediği uygulamalar.

Örneğin, 70 sentlik bir telefon ücretinde  5% vergi hesaplamak ondalık
kayan nokta ve ikili kayan nokta için farklı sonuçlar verir. Sonuçlar
en yakın küsurata yuvarlanırsa fark önemli hale gelir:

   >>> from decimal import *
   >>> round(Decimal('0.70') * Decimal('1.05'), 2)
   Decimal('0.74')
   >>> round(.70 * 1.05, 2)
   0.73

"Decimal" sonucu, iki basamaklı anlamlı çarpanlardan otomatik olarak
dört basamaklı anlamlılık çıkaran, sonunda bir sıfır tutar. Ondalık,
matematiği elle yapıldığı gibi yeniden üretir ve ikili kayan nokta
ondalık miktarları tam olarak temsil edemediğinde ortaya çıkabilecek
sorunları önler.

Tam gösterim, "Decimal" sınıfının, ikili kayan nokta için uygun
olmayan modlo hesaplamaları ve eşitlik testleri gerçekleştirmesini
sağlar:

   >>> Decimal('1.00') % Decimal('.10')
   Decimal('0.00')
   >>> 1.00 % 0.10
   0.09999999999999995

   >>> sum([Decimal('0.1')]*10) == Decimal('1.0')
   True
   >>> sum([0.1]*10) == 1.0
   False

"decimal" modülü, aritmetikle birlikte gerektiği kadar hassasiyet
sağlar:

   >>> getcontext().prec = 36
   >>> Decimal(1) / Decimal(7)
   Decimal('0.142857142857142857142857142857142857')
