7. Giriş ve Çıkış

Bir yazılımın çıktısını sunmanın birkaç yolu vardır; veri yazdırılabilir ya da gelecekte kullanılabilecek şekilde bir dosyaya kaydedilebilir. Bu bölümde giriş ve çıkış ile ilgili olanakların bazılarına değineceğiz.

7.1. Daha Güzel Çıkış Biçemi

Buraya kadar değerleri yazdırmanın iki yolunu gördük: deyim ifadeleri ve print deyimi. Üçüncü bir yol da dosya nesnelerinin write() yöntemidir. Standart çıktıya sys.stdout şeklinde atıfta bulunulabilir.

Çoğu zaman boşluklar ile birbirinden ayrılmış değerlerden daha iyi biçimlendirilimiş bir çıktıya ihtiyaç duyulur. Çıktınızı biçimlendirmenin iki yolu var. İlki bütün dizge işlemlerini dilimleme ve birleştirme ile yapıp istediğiniz herhangi bir biçimi elde etmek. string standart modülü dizgelerin istenen sütun genişliğine kadar boşluklar ile doldurulmasını sağlayan, daha sonra değineceğimiz, bazı faydalı işlevlere sahiptir. İkinci yol ise sol bağımsız değişkeni bir dizge olan % işlecini kullanmaktır. % işleci sol bağımsız değişkeni sağdaki bağımsız değişkenine uygulanacak sprintf() tarzı biçim dizgesi olarak yorumlar ve biçimleme işleminden sonra bir dizge geri döndürür.

Sayısal değerleri dizgeye çevirmek için ise değer repr() veya str() işlevine geçirilebilir ya da ters tırnak işareti (``) içine alınabilir (repr() ile aynı etkiye sahiptir).

str() işlevi değerlerin insan tarafından okunabilir gösterimini geri döndürürken, repr() işlevi yorumlayıcı tarafından okunabilir gösterimini geri döndürür (veya uygun sözdizim yok ise SyntaxError istisnası oluşturur). İnsan için anlam ifade edecek bir gösterimi bulunmayan nesneler için str() işlevi repr() ile aynı değeri döndürür. Rakamlar, listeler ve sözlükler gibi yapılar ile daha pek çok değer için her iki işlev de aynı sonucu verir. Dizgeler ve gerçel sayılar ise iki farklı gösterime sahiptir.

İşte birkaç örnek:

>>> s = 'Hello, world.'
>>> str(s)
'Hello, world.'
>>> `s`
"'Hello, world.'"
>>> str(0.1)
'0.1'
>>> `0.1`
'0.10000000000000001'
>>> x = 10 * 3.25
>>> y = 200 * 200
>>> s = 'The value of x is ' + `x` + ', and y is ' + `y` + '...'
>>> print s
The value of x is 32.5, and y is 40000...
>>> # Ters tırnaklar sayılar dışındaki tipler ile de çalışır:
... p = [x, y]
>>> ps = repr(p)
>>> ps
'[32.5, 40000]'
>>> # Karakter dizisinde ise tırnaklar ve ters bölü işareti eklenir:
... hello = 'hello, world\n'
>>> hellos = `hello`
>>> print hellos
'hello, world\n'
>>> # Ters tırnakların bağımsız değişkeni bir demet de olabilir:
... `x, y, ('spam', 'eggs')`
"(32.5, 40000, ('spam', 'eggs'))"

Sayıların kare ve küplerinden oluşan bir tablo yazdırmanın iki yolu vardır:

>>> import string
>>> for x in range(1, 11):
...     print string.rjust(`x`, 2), string.rjust(`x*x`, 3),
...     # Üst satırın sonundaki virgüle dikkat edin.
...     print string.rjust(`x*x*x`, 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
>>> for x in range(1,11):
...     print '%2d %3d %4d' % (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

Sütunların arasındaki bir karakterlik boşluk print tarafından eklenir; bağımsız değişkenlerin arasına daima bir boşluk karakteri eklenir.

Bu örnek dizgelerin başını boşluklar ile doldurup bunları sağ tarafa dayayan string.rjust() işlevini kullanmaktadır. Buna benzer string.ljust() ve string.center() işlevleri de vardır. Bunlar bir şey yazdırmaz; sadece yeni bir dizge geri döndürür. Verilen dizge uzun ise kırpılmaz ve aynen geri döndürülür; bu sütunlarınızın bozulmasına sebep olmasına rağmen hatalı bir değer göstermekten iyidir. Büyük bir değeri kırpmayı gerçekten istiyorsanız dilimleme ile bunu yapabilirsiniz (string.ljust(x, n)[0:n] gibi).

string.zfill() işlevi ise rakamlar içeren dizgelerin başını sıfırlar ile doldurur. Bu işlev artı ve eksi işaretlerini de dikkate alır:

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

% işleçi şu şekilde kullanılır:

>>> import math
>>> print 'PI sayısının yaklaşık değeri: %5.3f' % math.pi
PI sayısının yaklaşık değeri: 3.142

Dizgenin içinde birden fazla biçem varsa sağ terim olarak bir demet kullanmak gerekir:

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

Çoğu biçim aynı C dilindeki gibi çalışır ve doğru veri türünün geçirilmesi gerekir; bu yapılamaz ise bir istisna oluşur. %s biçiminin kullanımı daha rahattır; verilen bağımsız değişken dizge değilse yerleşik işlev str() ile dizgeye dönüştürülür. Genişlik ya da hassasiyeti belirtmek için * ile bir tamsayı bağımsız değişken kullanılabilir. C dilindeki %n ve %p biçimler ise desteklenmemektedir.

Eğer bölmek istemediğiniz gerçekten uzun bir biçim dizgeniz varsa biçimlendirmek istediğiniz bağımsız değişkenlere konumu yerine ismiyle atıfta bulunabilmeniz güzel olur. Bu aşağıda gösterildiği gibi %(isim)biçim şeklinde yapılabilir:

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

Bu özellik bütün yerel değişkenlerin bulunduğu bir sözlük geri döndüren yerleşik işlev vars() ile beraber kullanıldığında faydalı olur.

7.2. Dosya Okuma ve Yazma

open() işlevi bir dosya nesnesi geri döndürür ve genellikle iki bağımsız değişken ile kullanılır: open(dosya_adı, kip)

>>> f=open('/tmp/workfile', 'w')
>>> print f
<open file '/tmp/workfile', mode 'w' at 80a0960>

İlk bağımsız değişken dosya adını içeren bir dizgedir. İkincisi ise dosyanın nasıl kullanılacağını belirten karakterlerden oluşur. Erişim kipi dosyadan sadece okuma yapılacak ise 'r', sadece yazma için 'w' (aynı isimli bir dosya zaten var ise üzerine yazılır) ve dosyanın sonuna eklemeler yapmak için 'a' olur. 'r+' kipi dosyayı hem okuma hem de yazma yapmak için açar. kip bağımsız değişkeni seçimliktir; kullanılamaması halinde 'r' olduğu varsayılır.

Windows ve Macintosh üzerinde kipe eklenen 'b' harfi dosyayı ikilik kipte açar; yani 'rb', 'wb' ve 'r+b' gibi kipler de vardır. Windows metin ve ikilik dosyaları arasında ayrım yapmaktadır; metin dosyalarında okuma veya yazma işlemlerinde satır sonu karakterleri otomatik olarak biraz değişir. Bu görünmez değişiklik ASCII metin dosyaları için iyidir; anacak JPEG resimler veya .EXE dosyalar gibi iklik verileri bozar.

7.2.1. Dosya Nesnelerinin Yöntemleri

Bundan sonraki örneklerde f adlı bir dosya nesnesinin önceden oluşturulmuş olduğunu varsayacağız.

Dosyanın içeriğini okumak için belirli miktarda veriyi okuyup bunu dizge olarak geri döndüren f.read(boy)yöntemi kullanılabilir. boy okunacak bayt sayısını belirleyen seçimlik bir bağımsız değişkendir; kullanılmaması halinde dosyanın tamamı okunur. Dosyanın sonuna gelindiğinde f.read() boş bir dizge ("") geri döndürür.

>>> f.read()
'Dosyanın tamamı bu satırdan oluşuyor.\n'
>>> f.read()
''

f.readline() dosyadan tek bir satır okur. Satırın sonundaki satırsonu karakteri (\n) korunur; ancak dosya bir satırsonu karakteri ile bitmiyor ise son satırda bu karakter silinir. Bu özellik geri döndürülen değerin birden fazla anlama gelmesini engeller; f.readline() boş bir dizge geri döndürdüğünde dosyanın sonuna ulaşılırken boş bir satır tek bir '\n' karakteri ile ifade edilir.

>>> f.readline()
'Bu dosyanın ilk satırı.\n'
>>> f.readline()
'Dosyanın ikinci satırı\n'
>>> f.readline()
''

f.readlines() dosya içindeki bütün satırların bulunduğu bir liste geri döndürür. Seçimlik bağımsız değişken boy_ipucu kullanılması durumunda ise dosyadan boy_ipucu kadar ve bundan bir satır tamamlamaya yetecek kadar fazla bayt okunur ve bunlar yine satırlar listesi şeklinde geri döndürülür.

>>> f.readlines()
['Bu dosyanın ilk satırı.\n', 'Dosyanın ikinci satırı\n']

f.write(dizge) yöntemi dizge içeriğini dosyaya yazar ve None geri döndürür.

>>> f.write('Bu bir deneme satırıdır.\n')

f.tell() dosya nesnesinin dosya içindeki konumunu belirten bir tamsayı geri döndürür (dosyanın başından bayt cinsinden ölçülür). f.seek(uzaklık, nereden) ile de dosyanın içinde istenen konuma gidilebilir. Konum, uzaklık ile başvuru noktası nereden değerlerinin toplanması ile bulunur. nereden 0 olursa dosyanın başını, 1 o andaki konumu, 2 ise dosyanın sonunu belirtir. nereden kullanılmaz ise 0 olduğu varsayılır ve başvuru noktası olarak dosyanın başı alınır.

>>> f=open('/tmp/workfile', 'r+')
>>> f.write('0123456789abcdef')
>>> f.seek(5)     # Dosyadaki 5'inci bayta git
>>> f.read(1)
'5'
>>> f.seek(-3, 2) # Sondan 3'üncü bayta git
>>> f.read(1)
'd'

Dosya ile işiniz bittiğinde f.close() yöntemini çağırarak dosyayı kapatabilir ve dosyanın işgal ettiği sistem kaynaklarını serbest bırakabilirsiziz. f.close() çağrıldıktan sonra dosya üzerinde başka işlem yapmaya devam etmek mümkün değildir:

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

Dosya nesnelerinin isatty() ve truncate() gibi pek sık kullanılmayan başka yöntemleri de vardır.

7.2.2. pickle Modülü

Dizgeler kolayca dosyalara yazılıp dosyalardan okunabilir. Sayılar biraz zahmetlidir; çünkü read() yöntemi sadece dizgeleri geri döndürür ve bunların '123' gibi bir değeri alıp sayısal değeri 123'ü geri döndüren string.atoi() işlevinden geçirilmeleri gerekir. Listeler, sözlükler ve sınıf örnekleri gibi daha karmaşık veri türlerini dosyalara kaydetmek isterseniz işler oldukça zorlaşır.

Yazılımcıları karmaşık veri türlerini saklamak için kodlamak ve hata ayıklamak ile uğraştırmak yerine Python bu iş için pickle adlı standart modülü sağlar. Bu hayret verici modül neredeyse herhangi bir Python nesnesini (bazı Python kodu biçimlerini bile!) dizge ile ifade edilebilecek hale getirebilir ve bu halinden geri alabilir. Bu dönüşüm ve geri kazanım işlemleri arasında nesne bir dosyaya kaydedilebilir ya da ağ bağlantısı ile uzaktaki başka bir makineye gönderilebilir.

x gibi bir nesneniz ve yazma işlemi için açılmış f gibi bir dosya nesneniz varsa bu nesneyi dosyaya aktarmanız için tek satırlık kod yeterli olur:

pickle.dump(x, f)

Nesneyi geri almak için ise f okumak için açılmış bir dosya nesnesi olsun:

x = pickle.load(f)

Birden fazla nesnenin dönüştürülmesi gerekiyor ya da dönüştürülmüş olan nesnelerin dosyaya yazılması istenmiyor ise pickle farklı şekilde kullanılır. Bunları pickle modülünün belgelerinden öğrenmek mümkündür.

pickle modülü saklanabilen ve başka yazılımlar tarafından ya da aynı yazılımın farklı çalışma zamanlarında kullanılabilecek Python nesneleri yapmanın standart yoludur. pickle modülü çok yaygın kullanıldığından Python genişletme modülleri yazan çoğu yazılımcı matrisler gibi yeni veri türlerinin doğru olarak dönüştürülebilir ve geri alınabilir olmasına özen gösterir.