25 Ekim 2019 Cuma

Birkaç Pratik Bilgi

Buraya gelene kadar print() fonksiyonu ve bu fonksiyonun parametreleri hakkında epey söz söyledik. Dilerseniz şimdi de, programcılık maceranızda işinize yarayacak, işlerinizi kolaylaştıracak bazı ipuçları verelim.

Yıldızlı Parametreler

Şimdi size şöyle bir soru sormama izin verin: Acaba aşağıdaki gibi bir çıktıyı nasıl elde ederiz?
L.i.n.u.x
Aklınıza hemen şöyle bir cevap gelmiş olabilir:
>>> print("L", "i", "n", "u", "x", sep=".")

L.i.n.u.x
Yukarıdaki, gerçekten de doğru bir çözümdür. Ancak bu soruyu çözmenin çok daha basit bir yolu var. Şimdi dikkatle bakın:
>>> print(*"Linux", sep=".")

L.i.n.u.x
Konuyu açıklamaya geçmeden önce bir örnek daha verelim:
>>> print(*"Galatasaray")

G a l a t a s a r a y
Burada neler döndüğünü az çok tahmin ettiğinizi zannediyorum. Son örnekte de gördüğünüz gibi, “Galatasaray” karakter dizisinin başına eklediğimiz yıldız işareti; “Galatasaray” karakter dizisinin her bir öğesini parçalarına ayırarak, bunları tek tek print() fonksiyonuna yolluyor. Yani sanki print() fonksiyonunu şöyle yazmışız gibi oluyor:
>>> print("G", "a", "l", "a", "t", "a", "s", "a", "r", "a", "y")

G a l a t a s a r a y
Dediğimiz gibi, bir fonksiyona parametre olarak verdiğimiz bir karakter dizisinin başına eklediğimiz yıldız işareti, bu karakter dizisini tek tek öğelerine ayırıp, bu öğeleri yine tek tek ve sanki her bir öğe ayrı bir parametreymiş gibi o fonksiyona gönderdiği için doğal olarak yıldız işaretini ancak, birden fazla parametre alabilen fonksiyonlara uygulayabiliriz.
Örneğin len() fonksiyonu sadece tek bir parametre alabilir:
>>> len("Galatasaray")

11
Bu fonksiyonu birden fazla parametre ile kullanamayız:
>>> len("Galatasaray", "Fenerbahçe", "Beşiktaş")

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: len() takes exactly one argument (3 given)
Hata mesajında da söylendiği gibi, len() fonksiyonu yalnızca tek bir parametre alabilirken, biz 3 parametre vermeye çalışmışız...
Dolayısıyla yıldızlı parametreleri len() fonksiyonuna uygulayamayız:
>>> len(*"Galatasaray")

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: len() takes exactly one argument (11 given)
Bir parametrenin başına yıldız eklediğimizde, o parametreyi oluşturan bütün öğeler tek tek fonksiyona gönderildiği için, sanki len() fonksiyonuna 1 değil de, 11 ayrı parametre vermişiz gibi bir sonuç ortaya çıkıyor.
Yıldızlı parametreleri bir fonksiyona uygulayabilmemiz için o fonksiyonun birden fazla parametre alabilmesinin yanısıra, yapısının da yıldızlı parametre almaya uygun olması gerekir. Mesela open()type() ve biraz önce bahsettiğimiz len() fonksiyonlarının yapısı yıldızlı parametre almaya uygun değildir. Dolayısıyla yıldızlı parametreleri her fonksiyonla birlikte kullanamayız, ama print() fonksiyonu yıldızlı parametreler için son derece uygun bir fonksiyondur:
>>> print(*"Galatasaray")

G a l a t a s a r a y

>>> print(*"TBMM", sep=".")

T.B.M.M

>>> print(*"abcçdefgğh", sep="/")

a/b/c/ç/d/e/f/g/ğ/h
Bu örneklerden de gördüğünüz gibi, print() fonksiyonuna verdiğimiz bir parametrenin başına yıldız eklediğimizde, o parametre tek tek parçalarına ayrılıp print() fonksiyonuna gönderildiği için, sonuç olarak sep parametresinin karakter dizisi öğelerine tek tek uygulanmasını sağlamış oluyoruz.
Hatırlarsanız sep parametresinin öntanımlı değerinin bir adet boşluk karakteri olduğunu söylemiştik. Yani aslında Python yukarıdaki ilk komutu şöyle görüyor:
>>> print(*"Galatasaray", sep=" ")
Dolayısıyla, yıldız işareti sayesinde “Galatasaray” adlı karakter dizisinin her bir öğesinin arasına bir adet boşluk karakteri yerleştiriliyor. Bir sonraki “TBMM” karakter dizisinde ise, sep parametresinin değerini nokta işareti olarak değiştirdiğimiz için “TBMM” karakter dizisinin her bir öğesinin arasına bir adet nokta işareti yerleştiriliyor. Aynı şekilde “abcçdefgğh” karakter dizisinin her bir öğesini tek tek print() fonksiyonuna yollayarak, sep parametresine verdiğimiz / işareti yardımıyla her öğenin arasına bu / işaretini yerleştirebiliyoruz.
Yıldızlı parametrelerle ilgili tek kısıtlama, bunların sayılarla birlikte kullanılamayacak olmasıdır:
>>> print(*2345)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: print() argument after * must be a sequence, not int
Çünkü yıldızlı parametreler ancak ve ancak dizi özelliği taşıyan veri tipleriyle birlikte kullanılabilir. Mesela karakter dizileri bu türden bir veri tipidir. İlerde dizi özelliği taşıyan ve bu sayede yıldızlı parametrelerle birlikte kullanılabilecek başka veri tiplerini de öğreneceğiz.
Yukarıda verdiğimiz örnekler bize yıldızlı parametrelerin son derece kullanışlı araçlar olduğunu gösteriyor. İleride de bu parametrelerden bol bol yararlanacağız. Biz şimdi bu konuyu burada kapatıp başka bir şeyden söz edelim.

sys.stdout’u Kalıcı Olarak Değiştirmek

Önceki başlıklar altında verdiğimiz örneklerden de gördüğünüz gibi, print() fonksiyonunun file parametresi yardımıyla Python’ın standart çıktı konumunu geçici olarak değiştirebiliyoruz. Ama bazı durumlarda, yazdığınız programlarda, o programın işleyişi boyunca standart dışı bir çıktı konumu belirlemek isteyebilirsiniz. Yani standart çıktı konumunu geçici olarak değil, kalıcı olarak değiştirmeniz gerekebilir. Mesela yazdığınız programda bütün çıktıları bir dosyaya yazdırmayı tercih edebilirsiniz. Elbette bu işlemi her defasında file parametresini, çıktıları yazdırmak istediğiniz dosyanın adı olarak belirleyerek yapabilirsiniz. Tıpkı şu örnekte olduğu gibi:
>>> f = open("dosya.txt", "w")
>>> print("Fırat Özgül", file=f)
>>> print("Adana", file=f)
>>> print("Ubuntu", file=f)
>>> f.close()
Gördüğünüz gibi, her defasında file parametresine f değerini vererek işimizi hallettik. Ama bunu yapmanın daha pratik bir yöntemi var. Dilerseniz yazdığınız programın tüm işleyişi boyunca çıktıları başka bir konuma yönlendirebilirsiniz. Bunun için hem şimdiye kadar öğrendiğimiz, hem de henüz öğrenmediğimiz bazı bilgileri kullanacağız.
İlk önce şöyle bir kod yazalım:
>>> import sys
Bu kod yardımıyla sys adlı özel bir ‘modülü’ programımıza dahil etmiş, yani içe aktarmış olduk. Peki ‘modül’ nedir, ‘içe aktarmak’ ne demek?
Aslında biz bu ‘modül’ ve ‘içe aktarma’ kavramlarına hiç de yabancı değiliz. Önceki derslerde, pek üzerinde durmamış da olsak, biz Python’daki birkaç modülle zaten tanışmıştık. Mesela os adlı bir modül içindeki getcwd() adlı bir fonksiyonu kullanarak, o anda hangi dizinde bulunduğumuzu öğrenebilmiştik:
>>> import os
>>> os.getcwd()
Aynı şekilde keyword adlı başka bir modül içindeki kwlist adlı değişkeni kullanarak, hangi kelimelerin Python’da değişken adı olarak kullanılamayacağını da listeleyebilmiştik:
>>> import keyword
>>> keyword.kwlist
İşte şimdi de, os ve keyword modüllerine ek olarak sys adlı bir modülden söz ediyoruz. Gelin isterseniz öteki modülleri şimdilik bir kenara bırakıp, bu sys denen modüle dikkatimizi verelim.
Dediğimiz gibi, sys modülü içinde pek çok önemli değişken ve fonksiyon bulunur. Ancak bir modül içindeki değişken ve fonksiyonları kullanabilmek için o modülü öncelikle programımıza dahil etmemiz, yani içe aktarmamız gerekiyor. Bunu import komutuyla yapıyoruz:
>>> import sys
Artık sys modülü içindeki bütün fonksiyon ve değişkenlere ulaşabileceğiz.
sys modülü içinde bulunan pek çok değişken ve fonksiyondan biri de stdout adlı değişkendir. Bu değişkenin değerine şöyle ulaşabilirsiniz:
>>> sys.stdout
Bu komut şuna benzer bir çıktı verir:
<_io.TextIOWrapper name='<stdout>' mode='w' encoding='cp1254'>
Bu çıktıdaki name=’<stdout>’ kısmına dikkat edin. Bu ifadeye birazdan geri döneceğiz. Biz şimdi başka bir şeyden söz edelim.
Hatırlarsanız etkileşimli kabuğu nasıl kapatabileceğimizi anlatırken, etkileşimli kabuktan çıkmanın bir yolunun da şu komutları vermek olduğunu söylemiştik:
>>> import sys; sys.exit()
Bu komutu tek satırda yazmıştık, ama istersek şöyle de yazabiliriz elbette:
>>> import sys
>>> sys.exit()
Dedik ya, sys modülü içinde pek çok değişken ve fonksiyon bulunur. Nasıl stdout sys modülü içindeki değişkenlerden biri ise, exit() de sys modülü içinde bulunan fonksiyonlardan biridir.
Biz ‘modüller’ konusunu ilerleyen derslerde ayrıntılı bir şekilde inceleyeceğiz. Şimdilik modüllere ilişkin olarak yalnızca şunları bilelim yeter:
1. Python’da modüller import komutu ile içe aktarılır. Örneğin sys adlı modülü içe aktarmak için import sys komutunu veriyoruz.
2. Modüller içinde pek çok faydalı değişken ve fonksiyon bulunur. İşte bir modülü içe aktardığımızda, o modül içindeki bu değişken ve fonksiyonları kullanma imkanı elde ederiz.
3. sys modülü içindeki değişkenlere bir örnek stdout; fonksiyonlara örnek ise exit() fonksiyonudur. Bir modül içindeki bu değişken ve fonksiyonlara ‘modül_adı.değişken_ya_da_fonksiyon’ formülünü kullanarak erişebiliriz. Örneğin:
>>> sys.stdout
>>> sys.exit()
4. Hatırlarsanız bundan önce de, open() fonksiyonu ile dosya oluşturmayı anlatırken, oluşturulan dosyanın hangi dizinde olduğunu bulabilmek amacıyla, o anda içinde bulunduğumuz dizini tespit edebilmek için şu kodları kullanmıştık:
>>> import os
>>> os.getcwd()
Burada da os adlı başka bir modül görüyoruz. İşte os da tıpkı sys gibi bir modüldür ve tıpkı sys modülünde olduğu gibi, os modülünün de içinde pek çok yararlı değişken ve fonksiyon bulunur. getcwd() adlı fonksiyon da os modülü içinde yer alan ve o anda hangi dizin altında bulunduğumuzu gösteren bir fonksiyondur. Elbette, yine tıpkı sys modülünde olduğu gibi, os modülü içindeki bu yararlı değişken ve fonksiyonları kullanabilmek için de öncelikle bu os modülünü içe aktarmamız, yani programımıza dahil etmemiz gerekiyor. os modülünü import komutu aracılığıyla uygun bir şekilde içe aktardıktan sonra, modül içinde yer alan getcwd() adlı fonksiyona yine ‘modül_adı.fonksiyon’ formülünü kullanarak erişebiliyoruz.
Modüllere ilişkin şimdilik bu kadar bilgi yeter. Modülleri bir kenara bırakıp yolumuza devam edelim...
Eğer sys.exit() komutunu verip etkileşimli kabuktan çıktıysanız, etkileşimli kabuğa tekrar girin ve sys modülünü yeniden içe aktarın:
>>> import sys
Bir modülü aynı etkileşimli kabuk oturumu içinde bir kez içe aktarmak yeterlidir. Bir modülü bir kez içe aktardıktan sonra, o oturum süresince bu modül içindeki değişken ve fonksiyonları kullanmaya devam edebilirsiniz. Ama tabii ki etkileşimli kabuğu kapatıp tekrar açtıktan sonra, bir modülü kullanabilmek için o modülü tekrar içe aktarmanız gerekir.
Şimdi şu kodu yazın:
>>> f = open("dosya.txt", "w")
Bu kodun anlamını biliyorsunuz. Burada dosya.txt adlı bir dosyayı yazma kipinde açmış olduk. Tahmin edebileceğiniz gibi, çıktılarımızı ekran yerine bu dosyaya yönlendireceğiz.
Şimdi de şöyle bir kod yazalım:
>>> sys.stdout = f
Bildiğiniz gibi, sys.stdout değeri Python’ın çıktıları hangi konuma vereceğini belirliyor. İşte biz burada sys.stdout‘un değerini biraz önce oluşturduğumuz f adlı dosya ile değiştiriyoruz. Böylece Python bütün çıktıları f değişkeni içinde belirttiğimiz dosya.txt adlı dosyaya gönderiyor.
Bu andan sonra yazacağınız her şey dosya.txt adlı dosyaya gidecektir:
>>> print("deneme metni", flush=True)
Gördüğünüz gibi, burada file parametresini kullanmadığımız halde çıktılarımız ekrana değil, dosya.txt adlı bir dosyaya yazdırıldı. Peki ama bu nasıl oldu? Aslında bunun cevabı çok basit: Biraz önce sys.stdout = f komutuyla sys.stdout‘un değerini f değişkeninin tuttuğu dosya ile değiştirdik. Bu işlemi yapmadan önce sys.stdout‘un değeri şuydu hatırlarsanız:
<_io.TextIOWrapper name='<stdout>' mode='w' encoding='cp1254'>
Ama sys.stdout = f komutundan sonra her şey değişti. Kontrol edelim:
>>> print(sys.stdout, flush=True)
Elbette bu komuttan herhangi bir çıktı almadınız. Çıktının ne olduğunu görmek için dosya.txt adlı dosyayı açın. Orada şu satırı göreceksiniz:
<_io.TextIOWrapper name='dosya.txt' mode='w' encoding='cp1254'>
Gördüğünüz gibi, özgün stdout çıktısındaki name=’<stdout>’ değeri name=’dosya.txt’ olmuş. Dolayısıyla artık bütün çıktılar dosya.txt adlı dosyaya gidiyor...
Bu arada, yukarıdaki çıktıda görünen namemode ve encoding değerlerine şu şekilde ulaşabilirsiniz:
>>> sys.stdout.name
>>> sys.stdout.mode
>>> sys.stdout.encoding
Burada sys.stdout.name komutu standart çıktı konumunun o anki adını verecektir. sys.stdout.mode komutu ise standart çıktı konumunun hangi kipe sahip olduğunu gösterir. Standart çıktı konumu genellikle yazma kipinde (w) bulunur. sys.stdout.encoding kodu ise standart çıktı konumunun sahip olduğu kodlama biçimini gösterir. Kodlama biçimi, standart çıktı konumuna yazdıracağınız karakterlerin hangi kodlama biçimi ile kodlanacağını belirler. Kodlama biçimi Windows’ta genellikle ‘cp1254’, GNU/Linux’ta ise ‘utf-8’dir. Eğer bu kodlama biçimi yanlış olursa, mesela dosyaya yazdıracağınız karakterler içindeki Türkçe harfler düzgün görüntülenemez. Eğer burada söylediklerimiz size şu anda anlaşılmaz geliyorsa, söylediklerimizi dikkate almadan yolunuza devam edebilirsiniz. Birkaç bölüm sonra bu söylediklerimiz size daha fazla şey ifade etmeye başlayacak nasıl olsa.
Peki standart çıktı konumunu eski haline döndürmek isterseniz ne yapacaksınız? Bunun için etkileşimli kabuktan çıkıp tekrar girebilirsiniz. Etkileşimli kabuğu tekrar açtığınızda her şeyin eski haline döndüğünü göreceksiniz. Aynı şekilde, eğer bu kodları bir program dosyasına yazmış olsaydınız, programınız kapandığında her şey eski haline dönecekti.
Peki standart çıktı konumunu, etkileşimli kabuktan çıkmadan veya programı kapatmadan eski haline döndürmenin bir yolu var mı? Elbette var. Dikkatlice bakın:
>>> import sys
>>> f = open("dosya.txt", "w")
>>> sys.stdout, f = f, sys.stdout
>>> print("deneme", flush=True)
>>> f, sys.stdout = sys.stdout, f
>>> print("deneme")

deneme
Eğer yukarıdaki kodları çalıştıramıyorsanız, aynı etkileşimli kabuk oturumunda önceden verdiğiniz kodlar bu kodların doğru çıktı vermesini engelliyor olabilir. Bu sorunu aşmak için, etkileşimli kabuğu kapatıp tekrar açın ve yukarıdaki komutları tekrar verin.
Aslında burada anlayamayacağınız hiçbir şey yok. Burada yaptığımız şeyi geçen bölümlerde değişkenlerin değerini nasıl takas edeceğimizi anlatırken de yapmıştık. Hatırlayalım:
>>> osman = "Araştırma Geliştirme Müdürü"
>>> mehmet = "Proje Sorumlusu"
>>> osman, mehmet = mehmet, osman
Bu kodlarla Osman ve Mehmet’in unvanlarını birbiriyle takas etmiştik. İşte yukarıda yaptığımız şey de bununla aynıdır. sys.stdout, f = f, sys.stdout dediğimizde f değerini sys.stdout‘a, sys.stdout‘un değerini ise f‘ye vermiş oluyoruz. f, sys.stdout = sys.stdout, f dediğimizde ise, bu işlemin tam tersini yaparak her şeyi eski haline getirmiş oluyoruz.
Python’ın bize sunduğu bu kolaylıktan faydalanarak değişkenlerin değerini birbiriyle kolayca takas edebiliyoruz. Eğer böyle bir kolaylık olmasaydı yukarıdaki kodları şöyle yazabilirdik:
>>> import sys
>>> f = open("dosya.txt", "w")
>>> özgün_stdout = sys.stdout
>>> sys.stdout = f
>>> print("deneme", flush=True)
>>> sys.stdout = özgün_stdout
>>> print("deneme")

deneme
Gördüğünüz gibi, sys.stdout‘un değerini kaybetmemek için, sys.stdout değerini f adlı dosyaya göndermeden önce şu kod yardımıyla yedekliyoruz:
>>> özgün_stdout = sys.stdout
sys.stdout‘un özgün değerini özgün_stdout değişkenine atadığımız için, bu değere sonradan tekrar ulaşabileceğiz. Zaten yukarıdaki kodlardan da gördüğünüz gibi, sys.stdout‘un özgün değerine dönmek istediğimizde şu kodu yazarak isteğimizi gerçekleştirebiliyoruz:
>>> sys.stdout = özgün_stdout
Böylece stdout değeri eski haline dönmüş oluyor ve bundan sonra yazdırdığımız her şey yeniden ekrana basılmaya başlıyor.
...ve böylece uzun bir bölümü daha geride bıraktık. Bu bölümde hem print() fonksiyonunu bütün ayrıntılarıyla incelemiş olduk, hem de Python programlama diline dair başka çok önemli kavramlardan söz ettik. Bu bakımdan bu bölüm bize epey şey öğretti. Artık öğrendiğimiz bu bilgileri de küfemize koyarak başımız dik bir şekilde yola devam edebiliriz.

Kaynaklar:
Başer, M. "Python Programlama Dili." İstanbul: Pusula Yayınnları (2001).

https://belgeler.yazbel.com/python-istihza/

Karakter Dizilerini Alfabe Sırasına Dizmek Python’da karakter dizilerinin öğelerine tek tek ulaşma, öğeleri dilimleme ve ters çevirmenin...