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
================================

Şimdiye kadar iki değer yazma yolu ile karşılaştık: *expression
statements* ve "print()" fonksiyonu.  (Üçüncü bir yol, dosya
nesnelerinin "write()" yöntemini kullanmaktır; standart çıktı
dosyasına "sys.stdout" olarak başvurulabilir. Bu konuda daha fazla
bilgi için Kütüphane Referansı'na bakın.)

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'.

The "=" specifier can be used to expand an expression to the text of
the expression, an equal sign, then the representation of the
evaluated expression:

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

See self-documenting expressions for more information on the "="
specifier. For a reference on these format specifications, see the
reference guide for the Format Specification Mini-Language.


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

This could also be done by passing the "table" dictionary as keyword
arguments with the "**" notation.

   >>> 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.

As an example, the following lines produce a tidily aligned set of
columns giving integers and their squares and cubes:

   >>> 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 kesmiyorlar, ancak değiştirmeden döndürün; 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()" returns a *file object*, and is most commonly used with two
positional arguments and one keyword argument: "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.

Normally, files are opened in *text mode*, that means, you read and
write strings from and to the file, which are encoded in a specific
*encoding*. If *encoding* is not specified, the default is platform
dependent (see "open()"). Because UTF-8 is the modern de-facto
standard, "encoding="utf-8"" is recommended unless you know that you
need to use a different encoding. Appending a "'b'" to the mode opens
the file in *binary mode*. Binary mode data is read and written as
"bytes" objects. You can not specify *encoding* when opening file in
binary mode.

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.

Dosya nesnelerinin daha az kullanılan "isatty()" ve "truncate()" gibi
bazı ek metotları vardır; dosya nesneleri için eksiksiz bir kılavuz
için Kütüphane Referansı'na bakın.


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

Dizeler bir dosyaya kolayca yazılabilir ve dosyadan okunabilir.
Sayılar biraz daha fazla çaba gerektirir, çünkü "read()" yöntemi
yalnızca "'123'" gibi bir dize alan ve sayısal değeri 123'ü döndüren
"int()" gibi bir işleve geçirilmesi gereken dizeleri döndürür.  İç içe
geçmiş listeler ve sözlükler gibi daha karmaşık veri türlerini
kaydetmek istediğinizde, elle ayrıştırma ve seri hale getirmek
karmaşık hale gelir.

Rather than having users constantly writing and debugging code to save
complicated data types to files, Python allows you to use the popular
data interchange format called JSON (JavaScript Object Notation).  The
standard module called "json" can take Python data hierarchies, and
convert them to string representations; this process is called
*serializing*.  Reconstructing the data from the string representation
is called *deserializing*.  Between serializing and deserializing, the
string representing the object may have been stored in a file or data,
or sent over a network connection to some distant machine.

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)

To decode the object again, if "f" is a *binary file* or *text file*
object which has been opened for reading:

   x = json.load(f)

Not:

  JSON files must be encoded in UTF-8. Use "encoding="utf-8"" when
  opening JSON file as a *text file* for both of reading and writing.

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.
