7. Girdi ve Çıktı
*****************

Bir programın çıktısını sunmanın birkaç yolu vardır; veriler
okunabilir bir biçimde yazdırılabilir veya ileride kullanılmak üzere
bir dosyaya yazılabilir. Bu bölümde bazı olasılıklar tartışılacaktır.


7.1. Güzel Çıktı Biçimlendirmesi
================================

So far we've encountered two ways of writing values: *expression
statements* and the "print()" function.  (A third way is using the
"write()" method of file objects; the standard output file can be
referenced as "sys.stdout". See the Library Reference for more
information on this.)

Genellikle, yalnızca boşlukla ayrılmış değerleri yazdırmaktansa,
çıktınızın biçimlendirmesi üzerinde daha fazla denetim istersiniz.
Çıktıyı biçimlendirmenin birkaç yolu vardır.

* formatted string literals kullanmak için, açılış tırnak işaretinden
  veya üç tırnak işaretinden önce "f" veya "F" ile bir dize başlatın.
  Bu dizenin içinde, değişkenlere veya hazır bilgi değerlerine
  başvurabilen "{" ve "}" karakterleri arasında bir Python ifadesi
  yazabilirsiniz.

     >>> year = 2016
     >>> event = 'Referendum'
     >>> f'Results of the {year} {event}'
     'Results of the 2016 Referendum'

* "str.format()" dize yöntemi daha manuel çaba gerektirir.  Bir
  değişkenin değiştirileceği yeri işaretlemek için "{" ve "}"
  kullanmaya devam edersiniz ve ayrıntılı biçimlendirme yönergeleri
  sağlayabilirsiniz, ancak biçimlendirilecek bilgileri de sağlamanız
  gerekir.

     >>> yes_votes = 42_572_654
     >>> no_votes = 43_132_495
     >>> percentage = yes_votes / (yes_votes + no_votes)
     >>> '{:-9} YES votes  {:2.2%}'.format(yes_votes, percentage)
     ' 42572654 YES votes  49.67%'

* Son olarak, hayal edebileceğiniz herhangi bir düzen oluşturmak için
  dize dilimleme ve birleştirme işlemlerini kullanarak tüm dize
  işlemeyi kendiniz yapabilirsiniz.  Dize türü, dizeleri belirli bir
  sütun genişliğine doldurma için yararlı işlemler gerçekleştiren bazı
  yöntemlere sahiptir.

Daha güzel görünen bir çıktıya ihtiyacınız olmadığında, ancak hata
ayıklama amacıyla bazı değişkenlerin hızlı bir şekilde
görüntülenmesini istediğinizde, herhangi bir değeri "repr()" veya
"str()" işlevleriyle bir dizeye dönüştürebilirsiniz.

"str()" işlevi açıkça okunabilir değerlerin gösterimlerini döndürmek
için, "repr()" ise yorumlayıcı tarafından okunabilecek gösterimler
oluşturmak içindir (veya eş değer bir sözdizimi yoksa "SyntaxError" 'ı
zorlar).  İnsan tüketimi için belirli bir temsili olmayan nesneler
için "str()", "repr()" ile aynı değeri döndürür.  Sayılar veya
listeler ve sözlükler benzeri yapılar gibi birçok değer, her iki
işlevi de kullanarak aynı gösterime sahiptir.  Özellikle dizelerin iki
farklı gösterimi vardır.

Bazı örnekler:

   >>> s = 'Hello, world.'
   >>> str(s)
   'Hello, world.'
   >>> repr(s)
   "'Hello, world.'"
   >>> str(1/7)
   '0.14285714285714285'
   >>> x = 10 * 3.25
   >>> y = 200 * 200
   >>> s = 'The value of x is ' + repr(x) + ', and y is ' + repr(y) + '...'
   >>> print(s)
   The value of x is 32.5, and y is 40000...
   >>> # The repr() of a string adds string quotes and backslashes:
   ... hello = 'hello, world\n'
   >>> hellos = repr(hello)
   >>> print(hellos)
   'hello, world\n'
   >>> # The argument to repr() may be any Python object:
   ... repr((x, y, ('spam', 'eggs')))
   "(32.5, 40000, ('spam', 'eggs'))"

"string" modülü bir "Template" sınıfını içerir; bu sınıf, "$x" gibi
yer tutucuları kullanarak ve bunları bir sözlükten değerlerle
değiştirerek, değerleri dizelerle değiştirmenin başka bir yolunu
sunar, ancak biçimlendirme üzerinde çok daha az kontrol sağlar.


7.1.1. Biçimlendirilmiş Dize Değişmezleri
-----------------------------------------

Formatted string literals (kısaltmak için f-string olarak da
adlandırılır), dizenin önüne "f" veya "F" yazarak ve ifadeleri
"{expression}" olarak yazarak Python ifadelerinin değerini bir dizenin
içine eklemenize olanak tanır.

Opsiyonel biçim belirleyicisi ifadeyi izleyebilir. Bu, değerin nasıl
biçimlendirileceğini daha fazla denetlemenizi sağlar. Aşağıdaki örnek
pi sayısını ondalık sayıdan sonra üç basamağa yuvarlar:

   >>> import math
   >>> print(f'The value of pi is approximately {math.pi:.3f}.')
   The value of pi is approximately 3.142.

"':'" öğesinin ardından bir tamsayı geçmek, bu alanın en az sayıda
karakter genişliğinde olmasına neden olur.  Bu, sütunların hizaya
getirilmesi için yararlıdır.

   >>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
   >>> for name, phone in table.items():
   ...     print(f'{name:10} ==> {phone:10d}')
   ...
   Sjoerd     ==>       4127
   Jack       ==>       4098
   Dcab       ==>       7678

Diğer değiştiriciler, değeri biçimlendirilmeden önce dönüştürmek için
kullanılabilir. "'!a'" "ascii()", "'!s'" "str()", ve "'!r'"  "repr()"
uygular:

   >>> animals = 'eels'
   >>> print(f'My hovercraft is full of {animals}.')
   My hovercraft is full of eels.
   >>> print(f'My hovercraft is full of {animals!r}.')
   My hovercraft is full of 'eels'.

"=" belirleyicisi, bir ifadeyi ifadenin metnine, eşittir işaretine ve
ardından değerlendirilen ifadenin temsiline genişletmek için
kullanılabilir:

>>> bugs = 'roaches'
>>> count = 13
>>> area = 'living room'
>>> print(f'Debugging {bugs=} {count=} {area=}')
Debugging bugs='roaches' count=13 area='living room'

"=" belirtici hakkında daha fazla bilgi için kendi kendini belgeleyen
ifadeler konusuna bakın. Bu biçim belirtimleriyle ilgili bir referans
için, Format Specification Mini-Language için başvuru kılavuzuna
bakın.


7.1.2. String format() Metodu
-----------------------------

"str.format()" metodunun temel kullanımı şöyle görünür:

   >>> print('We are the {} who say "{}!"'.format('knights', 'Ni'))
   We are the knights who say "Ni!"

İçlerindeki köşeli ayraçlar ve karakterler (format fields olarak
adlandırılır) "str.format()" yöntemine geçirilen nesnelerle
değiştirilir.  Köşeli ayraçlardaki bir sayı, "str.format()" yöntemine
geçirilen nesnenin konumuna başvurmak için kullanılabilir.

   >>> print('{0} and {1}'.format('spam', 'eggs'))
   spam and eggs
   >>> print('{1} and {0}'.format('spam', 'eggs'))
   eggs and spam

Anahtar sözcük argümanları "str.format()" yönteminde kullanılıyorsa,
değerlerine argümanın adı kullanılarak başvurulmaktadır.

   >>> print('This {food} is {adjective}.'.format(
   ...       food='spam', adjective='absolutely horrible'))
   This spam is absolutely horrible.

Konumsal ve anahtar sözcük argümanları isteğe bağlı olarak
birleştirilebilir:

   >>> print('The story of {0}, {1}, and {other}.'.format('Bill', 'Manfred',
   ...                                                    other='Georg'))
   The story of Bill, Manfred, and Georg.

Bölmek istemediğiniz gerçekten uzun biçimli bir dizeniz varsa, konuma
göre değil de ada göre biçimlendirilecek değişkenlere başvurursanız
iyi olur.  Bu, sadece dict'i geçirerek ve tuşlara erişmek için "'[]'"
köşeli ayraçları kullanarak yapılabilir.

   >>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
   >>> print('Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; '
   ...       'Dcab: {0[Dcab]:d}'.format(table))
   Jack: 4098; Sjoerd: 4127; Dcab: 8637678

Bu, "table" sözlüğünü "**" gösterimiyle anahtar kelime bağımsız
değişkenleri olarak ileterek de yapılabilir.

   >>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
   >>> print('Jack: {Jack:d}; Sjoerd: {Sjoerd:d}; Dcab: {Dcab:d}'.format(**table))
   Jack: 4098; Sjoerd: 4127; Dcab: 8637678

Bu, özellikle tüm yerel değişkenleri içeren bir sözlük döndüren
yerleşik işlev "vars()" ile birlikte yararlıdır.

Örnek olarak, aşağıdaki satırlar, tamsayıları ve bunların karelerini
ve küplerini veren düzenli bir şekilde hizalanmış bir sütun kümesi
oluşturur:

   >>> for x in range(1, 11):
   ...     print('{0:2d} {1:3d} {2:4d}'.format(x, x*x, x*x*x))
   ...
    1   1    1
    2   4    8
    3   9   27
    4  16   64
    5  25  125
    6  36  216
    7  49  343
    8  64  512
    9  81  729
   10 100 1000

"str.format()" ile dize biçimlendirmeye tam bir genel bakış için bkz.
Format String Syntax.


7.1.3. Manuel Dize Biçimlendirmesi
----------------------------------

Manuel olarak biçimlendirilmiş aynı kare ve küp tablosu aşağıdadır:

   >>> for x in range(1, 11):
   ...     print(repr(x).rjust(2), repr(x*x).rjust(3), end=' ')
   ...     # Note use of 'end' on previous line
   ...     print(repr(x*x*x).rjust(4))
   ...
    1   1    1
    2   4    8
    3   9   27
    4  16   64
    5  25  125
    6  36  216
    7  49  343
    8  64  512
    9  81  729
   10 100 1000

(Her sütun arasındaki tek boşluğun "print()" çalışma şekliyle ekli
olduğunu unutmayın: her zaman argümanları arasına boşluk ekler.)

Dize nesnelerinin "str.rjust()" yöntemi, belirli bir genişlikteki bir
alandaki dizeyi soldaki boşluklarla doldurmayı haklı hale getirir.
Benzer yöntemler vardır "str.ljust()" ve "str.center()". Bu yöntemler
hiçbir şey yazmaz, yalnızca yeni bir dize döndürür. Giriş dizesi çok
uzunsa, onu kesmezler, değiştirmeden döndürürler; bu, sütununuzu
mahvedecektir, ancak bu genellikle bir değer hakkında yalan söylemek
olan alternatiften daha iyidir. (Gerçekten kesilme istiyorsanız,
"x.ljust(n)[:n]" gibi her zaman bir dilim işlemi ekleyebilirsiniz.)

Soldaki sayısal bir dizeyi sıfırlarla dolduran başka bir metot vardır:
"str.zfill()".  Bu metot artı ve eksi işaretlerini anlar:

   >>> '12'.zfill(5)
   '00012'
   >>> '-3.14'.zfill(7)
   '-003.14'
   >>> '3.14159265359'.zfill(5)
   '3.14159265359'


7.1.4. Eski dize biçimlendirmesi
--------------------------------

% operatör (modulo) dize biçimlendirmesi için de kullanılabilir.
"'string' % values" göz önüne alındığında, "string" öğesindeki "%"
örnekleri sıfır veya daha fazla "values" öğesiyle değiştirilir. Bu
işlem genellikle dize enterpolasyonu olarak bilinir. Mesela:

   >>> import math
   >>> print('The value of pi is approximately %5.3f.' % math.pi)
   The value of pi is approximately 3.142.

Daha fazla bilgiyi printf-style String Formatting bölümünde
bulabilirsiniz.


7.2. Dosyaları Okuma ve Yazma
=============================

"open()" bir *file object* döndürür ve en yaygın olarak iki konum
argümanı ve bir anahtar sözcük argümanı ile kullanılır:
"open(filename, mode, encoding=None)"

   >>> f = open('workfile', 'w', encoding="utf-8")

İlk parametre dosya adını içeren bir dizedir.  İkinci parametre
dosyanın nasıl kullanılacağını açıklayan birkaç karakter içeren başka
bir dizedir.  *mode*, dosya yalnızca okunacağı zaman "'r'", yalnızca
yazmak için "'w'" olabilir (aynı ada sahip varolan bir dosya
temizlenir) ve "'a'" dosyayı ekleme için açar; dosyaya yazılan tüm
veriler otomatik olarak sonuna eklenir.  "'r+'" dosyayı hem okumak hem
de yazmak için açar. *mode* parametresi isteğe bağlıdır; verilmezse
"'r'" varsayılacaktır.

Normalde dosyalar *text mode* 'unda açılır, yani belirli bir
*kodlamada (encoding)* kodlanmış dizeleri dosyadan okur ve dosyaya
yazarsınız. *kodlama* belirtilmezse, varsayılan değer platforma
bağlıdır (bakınız "open()"). UTF-8 modern de-fakto standart
olduğundan, farklı bir kodlama kullanmanız gerekmiyorsa
"encoding="utf-8"" kullanmanız önerilir. Moda "'b'" eklemek, dosyayı
*binary* modunda açar. İkili mod verileri, "bytes" nesneleri olarak
okunur ve yazılır. Dosyayı ikili modda açarken *kodlama*
belirtemezsiniz.

Metin modunda, okurken varsayılan değer platforma özgü satır sonlarını
("\n" on Unix, "\r\n" on Windows) yalnızca "\n" olarak dönüştürmektir.
Metin modunda yazarken, varsayılan değer "\n" oluşumlarını platforma
özgü satır sonlarına geri dönüştürmektir.  Dosya verilerinde yapılan
bu sahne arkası değişikliği metin dosyaları için iyidir, ancak "JPEG"
veya "EXE" dosyalarında bunun gibi ikili verileri bozacaktır.  Bu tür
dosyaları okurken ve yazarken ikili modu kullanmaya çok dikkat edin.

Dosya nesneleriyle uğraşırken "with" anahtar sözcüğünü kullanmak iyi
bir uygulamadır.  Avantajı, herhangi bir noktada bir hata oluşsa bile,
paketi bittikten sonra dosyanın düzgün bir şekilde kapatılmasıdır.
"with" kullanmak da eş değer "try" -"finally" blokları yazmaktançok
daha kısadır:

   >>> with open('workfile', encoding="utf-8") as f:
   ...     read_data = f.read()

   >>> # We can check that the file has been automatically closed.
   >>> f.closed
   True

"with" anahtar sözcüğünü kullanmıyorsanız, dosyayı kapatmak ve
kullandığı sistem kaynaklarını hemen boşaltmak için "f.close()"
metodunu çağırmalısınız.

Uyarı:

  "with" anahtar sözcüğünü kullanmadan "f.write()" çağırmak veya
  "f.close()" çağırmak, program başarıyla çıksa bile "f.write()"
  parametrelerinin diske tamamen yazılmamasıyla sonuçlanabilir.

Bir dosya nesnesi kapatıldıktan sonra, bir "with" deyimiyle veya
"f.close()" çağırarak dosya nesnesini kullanma girişimleri otomatik
olarak başarısız olur.

   >>> f.close()
   >>> f.read()
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   ValueError: I/O operation on closed file.


7.2.1. Dosya Nesnelerinin Metotları
-----------------------------------

Bu bölümdeki örneklerin geri kalanı, "f" adlı bir dosya nesnesinin
zaten oluşturulduğunu varsayar.

Bir dosyanın içeriğini okumak için, bir miktar veriyi okuyan ve dize
(metin modunda) veya bayt nesnesi (ikili modda) olarak döndüren
"f.read(size)" öğesini çağırın. *size* isteğe bağlı bir sayısal
parametredir.  *size* boş bırakıldığında veya negatif olduğunda,
dosyanın tüm içeriği okunur ve döndürülür; dosya makinenizin
belleğinden iki kat daha büyükse bu sizin sorununuzdur. Aksi takdirde,
en fazla *size* karakterleri (metin modunda) veya *size* bayt (ikili
modda) okunur ve döndürülür. Dosyanın sonuna ulaşıldıysa, "f.read()"
boş bir dize ("''") döndürür.

   >>> f.read()
   'This is the entire file.\n'
   >>> f.read()
   ''

"f.readline()" dosyadan tek bir satır okur; dizenin sonunda bir
newline karakteri ("\n") bırakılır ve dosya yalnızca dosya yeni
satırda bitmezse dosyanın son satırında atlanır.  Bu, dönüş değerini
netleştirir; "f.readline()" boş bir dize döndürürse, dosyanın sonuna
ulaşılmış demektir, boş bir satır ise yalnızca tek bir yeni satır
içeren bir dize olan "'\n'" ile temsil edilir.

   >>> f.readline()
   'This is the first line of the file.\n'
   >>> f.readline()
   'Second line of the file\n'
   >>> f.readline()
   ''

Bir dosyadan satırları okumak için, dosya nesnesinin üzerinde döngü
oluşturabilirsiniz. Bu bellek verimliliğine, hızlılığına ve basit koda
yol açar:

   >>> for line in f:
   ...     print(line, end='')
   ...
   This is the first line of the file.
   Second line of the file

Listedeki bir dosyanın tüm satırlarını okumak istiyorsanız, "list(f)"
veya "f.readlines()" öğelerini de kullanabilirsiniz.

"f.write(string)" *string* içeriğini dosyaya yazar ve yazılan karakter
sayısını döndürür.

   >>> f.write('This is a test\n')
   15

Diğer nesne türlerinin yazmadan önce bir dizeye (metin modunda) veya
bayt nesnesine (ikili modda) dönüştürülmesi gerekir:

   >>> value = ('the answer', 42)
   >>> s = str(value)  # convert the tuple to string
   >>> f.write(s)
   18

"f.tell()" dosya nesnesinin dosyadaki geçerli konumunu ikili moddayken
dosyanın başından itibaren bayt sayısı ve metin modundayken opak bir
sayı olarak veren bir tamsayı döndürür.

Dosya nesnesinin konumunu değiştirmek için "f.seek(offset, whence)"
kullanın.  Konum, bir referans noktasına *offset* eklenerek
hesaplanır; referans noktası *whence* parametresi tarafından seçilir.
*whence* değeri dosyanın başından itibaren 0 ölçerken, 1 geçerli dosya
konumunu, 2 ise başvuru noktası olarak dosyanın sonunu kullanır.
*whence* atlanabilir ve başvuru noktası için dosyanın başlangıcını
kullanarak 0 olarak varsayılabilir.

   >>> f = open('workfile', 'rb+')
   >>> f.write(b'0123456789abcdef')
   16
   >>> f.seek(5)      # Go to the 6th byte in the file
   5
   >>> f.read(1)
   b'5'
   >>> f.seek(-3, 2)  # Go to the 3rd byte before the end
   13
   >>> f.read(1)
   b'd'

Metin dosyalarında (mod dizesinde "b" olmadan açılanlar), yalnızca
dosyanın başına göre aramalara izin verilir (dosyanın sonuna "seek(0,
2)" ile arayan özel durum) ve tek geçerli *offset* değerleri
"f.tell()" veya sıfırdan döndürülen değerlerdir. Başka herhangi bir
*offset* değeri tanımsız davranış üretir.

File objects have some additional methods, such as "isatty()" and
"truncate()" which are less frequently used; consult the Library
Reference for a complete guide to file objects.


7.2.2. Yapılandırılmış verileri "json" ile kaydetme
---------------------------------------------------

Strings can easily be written to and read from a file.  Numbers take a
bit more effort, since the "read()" method only returns strings, which
will have to be passed to a function like "int()", which takes a
string like "'123'" and returns its numeric value 123.  When you want
to save more complex data types like nested lists and dictionaries,
parsing and serializing by hand becomes complicated.

Python, kullanıcıların karmaşık veri türlerini dosyalara kaydetmek
için sürekli olarak kod yazmasını ve hata ayıklamasını sağlamak
yerine, JSON (JavaScript Object Notation) adlı popüler veri değişim
biçimini kullanmanıza olanak tanır.  "json" adlı standart modül,
Python veri hiyerarşilerini alabilir ve bunları dize temsillerine
dönüştürebilir; bu işleme *serializing* adı verilir.  Dize
gösteriminden verilerin yeniden yapılandırılmasına *deserializing*
denir.  Serileştirme ve seri durumdan çıkarma arasında, nesneyi temsil
eden dizi bir dosyada veya veride saklanmış olabilir veya bir ağ
bağlantısı üzerinden uzaktaki bir makineye gönderilmiş olabilir.

Not:

  JSON biçimi, veri alışverişine izin vermek için modern uygulamalar
  tarafından yaygın olarak kullanılır.  Birçok programcı zaten buna
  aşinadır, bu da onu birlikte çalışabilirlik için iyi bir seçim
  haline getirir.

"x" nesnesiniz varsa, JSON dize gösterimini basit bir kod satırıyla
görüntüleyebilirsiniz:

   >>> import json
   >>> x = [1, 'simple', 'list']
   >>> json.dumps(x)
   '[1, "simple", "list"]'

"dumps()" işlevinin başka bir çeşidi, "dump()" adı verilen nesneyi bir
*text file* (metin dosyası) olarak seri hale getirmektedir.  Yani "f"
bir *text file* nesnesi yazmak için açılmışsa, bunu yapabiliriz:

   json.dump(x, f)

Nesnenin kodunu tekrar çözmek için, "f" okuma için açılmış bir *binary
file* veya *text file* nesnesiyse:

   x = json.load(f)

Not:

  JSON dosyaları UTF-8'de kodlanmalıdır. Hem okuma hem de yazma için
  JSON dosyasını *text file* olarak açarken "encoding="utf-8""
  kullanın.

Bu basit seri hale getirme tekniği listeleri ve sözlükleri
işleyebilir, ancak JSON'da rasgele sınıf örneklerini seri hale
getirmek biraz daha fazla çaba gerektirir. "json" modülü için olan
örnek bunun bir açıklamasını içerir.

Ayrıca bakınız:

  "pickle" - pickle modülü

  JSON ifadesinin aksine, *pickle*, gelişigüzel olarak karmaşık Python
  nesnelerinin seri hale getirilmesine izin veren bir protokoldür.  Bu
  nedenle, Python'a özgüdür ve diğer dillerde yazılmış uygulamalarla
  iletişim kurmak için kullanılamaz.  Varsayılan olarak da
  güvensizdir: güvenilmeyen bir kaynaktan gelen pickle verilerinin
  dizilerinin seri halden çıkarılması, veriler yetenekli bir saldırgan
  tarafından hazırlanmışsa rasgele kod yürütebilir.
