3.2. Kullanıcıdan Veri Alma

Kayıt Tarihi:

Son Güncelleme:

Özet:

Bu ders biraz uzun olacak, programın kullanıcı ile iletişim kurabilmesinin tüm yollarını tek bir derste açıklamak istedim. Bu yollar şunlar: programın kullanıcıya sorular sorarak veri alması, kullanıcının programı çalıştırırken komut satırından programa veri iletmesi, programın kullanıcının oluşturduğu bir dosyadan veriler alması ve programın bir dosyaya yazarak kullanıcıya veri iletmesi. Ayrıca bu derste kullanıcının hatalı veri iletmesi sonucu oluşabilecek hataları kontrol etmenin de yollarını öğreneceksiniz.

Anahtar Kelimeler: argparse · close · dosya okuma yazma · eval · exception · exec · file · hata kontrolü · indexerror · nameerror · open · raise · raw_input · read · readlines · Simpson kuralı · split · sys.argv · terminal · try-except · typeerror · valueerror · with · zerodivisionerror

Yazdığımız programlarda değişkenlere yapılacak değer atamalarını kullanıcıdan almak isteyebiliriz. Bu durumda program ile kullanıcı etkileşim kurarak veri akışı sağlanmalıdır. Programın kullanıcıdan veri almasının üç temel yolu vardır. Program çalışırken gerekli olan durumlarda kullanıcıya soru sorup aldığı cevabı veri olarak işleyebilir, ya da kullanıcı Terminalden programı çalıştırırken komut satırında bazı verilerin programa aktarılmasını sağlayabilir. Diğer yöntem ise kullanıcının programa iletmek istediği verileri bir dosyada kaydetmesi ve programın bu dosyayı okumasıdır. Bu bölümde bu yöntemleri ele alacağız. Kullanıcıdan veri almanın bazı riskleri vardır, kullanıcı programın beklemediği tipte veya formatta veriler ileterek programın hata üretmesini sağlayabilir. Böyle durumları kontrol etmek için programımızda hata kontrölü yapmalıyız, bölüm içinde bu konuya da değineceğiz.

Kullanıcıya Soru Sorma

Programa kullanıcının ilettiği verilere kullanıcı girişi (user input) deriz, bir programa kullanıcı girişi sağlamanın en basit yolu programın kullanıcıya soru sormasını sağlamaktır. Python'da bu işi raw_input() fonksiyonu ile yaparız, bu fonksiyon aldığı string tipi argümanı ekrana yazdırarak kullanıcının klavye ile bir veri girip enter tuşuna basmasını bekler.


from math import pi

r = raw_input("Yaricap Girin: ")
r = float(r)
A = pi*r**2
print "Yaricapi %g olan daire alani: %g" % (r, A)

Bu örnekte de raw_input() fonksiyonunun kullanımını gözlemleyin. Bu fonksiyonu çalıştırınca aşağıdaki çıktıda görüleceği gibi ekrana Yaricap Girin: mesajı yazdırılır ve program bekler. Kullanıcı bir sayı girip enter tuşuna basınca diğer komutlar kaldığı yerden çalıştırılmaya devam eder. Burada dikkat edilmesi gereken husus şudur, raw_input() komutu ile kullanıcıdan alınan veriler her zaman bir string nesnesi olarak alınır. Dolayısıyla bu veriyle işlem yapmadan önce bunu uygun nesnelere dönüştürmemiz gerekir, burada bunu bir float nesnesine dönüştürdük.


Terminal > python raw1.py
Yaricap Girin: 2.75
Yaricapi 2.75 olan daire alani: 23.7583

Başka bir örnek olarak daha önce ele aldığımız $y(t)=v0t-0.5gt^2$ foksiyonunu hesaplatalım.


v0 = float(raw_input("ilk hiz= "))
t = float(raw_input("zaman= "))
g = raw_input("yer cekimi ivmesi= ")
g = float(g) if g else 9.81
y = lambda v0, t, g: v0*t - 0.5*g*t**2
print y(v0, t, g)

Bu programın çıktısı aşağıdaki gibi olur. Satır içi if testini nasıl kurduğumuza dikkat edin. Burada dallanmayı parantez içinde yapmak zorunda olmadığımızı anlıyoruz. Ayrıca if g ifadesinde g nesnesini nasıl bool nesnesi olarak kullandığımıza da dikkat edin. Hatırlayın, boş stringler False bool değerine sahiptir. Program ilk çalıştırıldığında kullanıcı yercekimi ivmesi= sorusuna cevap vermeden enter tuşuna basıyor, burada program cevabı boş bir string nesnesi olarak algılıyor.


Terminal > python raw2.py
ilk hiz= 2
zaman= 0.1
yer cekimi ivmesi=
0.15095
Terminal > python raw2.py
ilk hiz= 2
zaman= 0.1
yer cekimi ivmesi= 1.62
0.1919

Terminal Parametreleri

Terminal uygulamasından programlarımızı nasıl çalıştırdığımızı hatırlayalım, komut satırına python program.py komutunu giriyorduk. Bu komutla aslında işletim sistemi komut istemcisine, python isimli yazılımı program.py parametresi ile çalıştırması emrini veriyoruz. Python yazılımının görevi, kendisine verilen ilk parametrede yolu belirtilen dosya içindeki komutları çalıştırıp ekranda çıktıları göstermektir. Python yazılımı birden fazla parametre ile çalıştırılıp, program isminden sonraki parametre değerlerini çalıştırdığımız programa iletebilir. Böylece kullanıcı bazı verileri, programı komut satırından çalıştırırken girerek programa iletebilir.

Python'da sys modülü içinde argv isimli bir list nesnesi tanımlıdır, bu listede tüm komut satırı parametreleri saklanır. sys.argv[0] elemanı her zaman programın ismidir, sonrasında gelen elemanlar da sırasıyla diğer komut satırı parametreleridir. Yine bu parametreler de birer string nesnesi olarak alınır, dolayısıyla uygun dönüşümler yapılarak kullanılmalıdır.


import sys

t = sys.argv[1]
t = float(t); v0 = 2; g = 9.81
y = v0*t - 0.5*g*t**2
print y

Bu örnekte de görüldüğü gibi argv listesine erişmek için import sys komutu ile sys modülüne erişim sağlamalıyız. Daha sonra komut satırında program isminden sonra girilen ilk argümanı t değişkenine atıyoruz, bu bir string verisi olacağından sonrasında bunu float nesnesine çevirerek programa devam ediyoruz. Kullanıcı bu programı çalıştırırken program isminin arkasından bir boşluk bırakıp bir sayı yazıp enter tuşuna basmalıdır. Kullanıcı program isminden sonra bir float nesnesine çevrilebilecek bir argüman girmezse hata oluşacaktır.


Terminal > python sysargv1.py 0.1
0.15095

Komut satırında birdan fazla arügman ile programa parametreler aktarılabilir. Kullanıcının aralarında boşluk bırakarak girdiği her veri yeni bir argüman olarak sys.argv listesinde yerini alacaktır. Dolayısıya tüm parametrelere sys.argv[1], sys.argv[2], ve benzer şekilde erişilebilir.


import sys

g = 9.81
v0 = float(sys.argv[1])
t = float(sys.argv[2])
y = v0*t - 0.5*g*t**2
print y

Bu programı çalıştırırken kullanıcı program isminden sonra aralarında boşluk bırakarak iki sayı girmelidir. Daha az parametre girerse veya float nesnesine çevrilemeyecek bir veri girerse program hata verecektir.


Terminal > python sysargv2.py 2 0.1
0.15095

Komut satırı argümanları girerken dikkat etmemiz gereken bir nokta var. Sistem boşluk ile ayrılmış her veriyi ayrı bir parametre olarak kaydettiğinden, boşluklar da içeren bir string nesnesini parametre olarak girerken dikkatli olunmalıdır. Böyle bir veriyi kullanıcı tırnak işaretleri arasında girmelidir, aksi taktirde girilen metnin her bir kelimesi ayrı bir parametre olarak algılanır.


import sys

metin = sys.argv[1:]

print "girilen parametrelerin listesi:"
print metin

Bu programı çalıştırırken üç kelimeden oluşan bir string nesnesi girelim. Bunu tırnak içinde yazmazsak her bir kelimesi ayrı bir argüman olarak değerlendirelecektir. Tek bir argüman olarak algılanmasını sağlamak için tırnak işaretleri kullanmalıyız.


Terminal > python sysargv3.py bu bir metin
girilen parametrelerin listesi:
['bu', 'bir', 'metin']
Terminal > python sysargv3.py "bu bir metin"
girilen parametrelerin listesi:
['bu bir metin']

Komut satırından varsayılan değerli argümanlar da alınabilir, bu işlem için Python'da argparse modülü kullanılır. Bu modülün kullanımını aşağıdaki örnek üzerinde açıklayacağız.


import argparse
parser = argparse.ArgumentParser()

parser.add_argument('--v0', type=float, default=2.0, help='ilk hiz')
parser.add_argument('--g', type=float, default=9.81, help='yer cekimi')
parser.add_argument('--t', type=float, default=0.1, help='zaman')

args = parser.parse_args()

v0 = args.v0
g = args.g
t = args.t
s = v0*t - 0.5*g*t**2
print s

Öncelikle argparse modülüne erişip ArgumentParser() komutuyla bir parser nesnesi oluşturuyoruz, bu nesne ile tanımlanacak argümanları komut satırından okuyacağız. Daha sonra bu nesnenin üzerinde tanımlı add_argument() komutuyla programda kullanacağımız tüm komut satrı argümanlarını tanımlıyoruz. Değişkenlerin isimlerini, tiplerini, varsayılan değerlerini ve yardım metnini örnekteki gibi gireriz. Bundan sonra bu nesnenin parse.args() komutunu kullanarak programımızın argümanları komut satırından okuyarak args isimli yeni bir nesne içinde saklamasını sağlıyoruz. Artık komut satırından girilen varsayılan değerli argümanlar program içinde args.v0 biçiminde erişilebilir durumdadır. Kullanıcı komut satırından bir argüman girmek isterse bunu --v0 2.0 --t 0.1 --g 9.81 biçiminde girebilir. Ayrıca argparse modülü hazırladığımız programlarımız için bir de yardım metni oluşturur, bu metni görüntülemek için programı -h parametresiyle çalıştırmak yeterlidir.


Terminal > python sysargv4.py
0.15095
Terminal > python sysargv4.py --g 1.62
0.1919
Terminal > python sysargv4.py --g 1.62 --t 2
0.76
Terminal > python sysargv4.py --g 1.62 --t 1 --v0 15
14.19
Terminal > python sysargv4.py -h
usage: sysargv4.py [-h] [--v0 V0] [--g G] [--t T]

optional arguments:
-h, --help  show this help message and exit
--v0 V0     ilk hiz
--g G       yer cekimi
--t T       zaman

Eval ve Exec Fonksiyonları

Kullanıcı raw_input() veya komut satırı argümanları aracılığıyla alınan veriler string nesnesi olarak alınır ve daha programda hesaplamalara dahil edilmeden önce uygun nesnelere dönüştürülür. Fakat bu yolla kullanıcı örneğin pi verisini programa aktaramaz.


>>> from math import pi
>>> s = "pi"
>>> type(s)
type 'str'>
>>> float(s)
Traceback (most recent call last):
File "", line 1, in 
ValueError: could not convert string to float: pi

Böyle durumlarda eval() (evaluate) fonksiyonundan faydalanırız. s bir string nesnesi olmak üzere x = eval(s) komutu Python'da x = s kodunu çalıştırı. Dolayısıyla eğer s string nesnesi geçerli bir Python ifadesi biçiminde bir string nesnesi ise, hatasız bir atama gerçekleşecektir.


>>> from math import pi, sqrt
>>> s1 = "pi"
>>> s2 = "sqrt(2)"
>>> x = eval(s1)
>>> x
3.141592653589793
>>> y = eval(s2)
>>> y
1.4142135623730951

eval() fonksiyonu aldığı string argümanı önce bir Python ifadesi olarak çalıştırıp bir veriye dönüştürdükten sonra döndürür. Yukarıdaki örnekte bunu gözlemleyebiliyoruz. Benzer şekilde x = eval("1 + 2") komutu arka planda doğrudan x = 3 komutunu çalıştırır.


from math import *              # > program.py "sin(x) + sqrt(2*x)" pi
import sys                       

f = sys.argv[1]                 # f = "sin(x) + sqrt(2*x)"
x = eval(sys.argv[2])           # x = pi
y = eval(f)                     # y = sin(x) + sqrt(2*x)
print "y=f(x)=%s, x=%s -> y=%g" % (f, x, y)

Yukarıdaki örnekte eval() fonksiyonunun ne kadar kullanışlı olduğunu gözlemleyebiliriz. $y=f(x)$ biçiminde bir fonksiyonun hem formülünü hem de değişken değerini kullanıcı fonksiyona sağlayabilir. Burada kullanıcı fonksiyonu komut satırında tırnak işareti içinde girmelidir çünkü parantez sembolleri komut satırı için anlamlı sembollerdir ve düz string olarak algılanmaz.


Terminal > python sysargv5.py "sin(x) + sqrt(2*x)" pi
y=f(x)=sin(x) + sqrt(2*x), x=3.14159265359 -> y=2.50663

Python'da eval() fonksiyonuna benzer olan bir de exec() (execute) fonksiyonu vardır. Bu fonksiyon da argüman olarak aldığı string nesnesini Python koduna çevirir fakat bu fonksiyon çok satırdan oluşan string nesnelerini de alabilir. Örneğin aşağıdaki program da yukarıda yazdığımız programla aynı çıktıyı üretir. Fakat bu program kullanıcının verdiği formülü kullanarak bir fonksiyon tanımlar.


from math import *
import sys

formul = sys.argv[1]

s = """
def f(x):
        return %s
""" % formul

exec(s)
x = eval(sys.argv[2])
y = f(x)

print "y=f(x)=%s, x=%s -> y=%g" % (formul, x, y)

Başka bir örnek olarak daha önce ele aldığımız Simpson kuralı ile yaklaşık integrasyon yapan programımızı tekrar ele alalım. exec() fonksiyonu yardımıyla kullanıcıdan verileri alabiliriz.


from math import *
import sys

def Simpson(f, a, b, n=100):
    h = (b-a)/float(n); toplam1 = 0; toplam2 = 0
    
    for i in range(1, n/2 + 1):
        toplam1 += f(a + (2*i-1)*h)
    
    for i in range(1, n/2):
        toplam2 += f(a + 2*i*h)
    
    integral = (h/3)*(f(a) + f(b) + 4*toplam1 + 2*toplam2)
    return integral

formul , a, b = sys.argv[1:]
a = eval(a); b = eval(b)

s = """
def f(x):
    return %s
""" % formul

exec(s)

print "f(x)=%s, a=%s, b=%s" % (formul, a, b)
for n in 2, 5, 10, 50, 100, 500, 1000:
    hesap = Simpson(f, a, b, n)
    kesin = 4.0/3
    hata = abs(hesap - kesin)
    print "n = %4d, Hesaplanan=%.12f, Hata=%g" % (n, hesap, hata)

Bu durumda kullanıcı komut satırından fonksiyonu ve integrasyon sınırlarını programa verir. Bu program da gerekli hesaplamaları yapıp aşağıdaki gibi bir çıktı üretir.


Terminal > python simpson4.py "sin(x)**3" 0 pi
f(x)=sin(x)**3, a=0, b=3.14159265359
n =    2, Hesaplanan=2.094395102393, Hata=0.761062
n =    5, Hesaplanan=1.251135387587, Hata=0.0821979
n =   10, Hesaplanan=1.332599724263, Hata=0.000733609
n =   50, Hesaplanan=1.333332289401, Hata=1.04393e-06
n =  100, Hesaplanan=1.333333268318, Hata=6.50158e-08
n =  500, Hesaplanan=1.333333333229, Hata=1.03907e-10
n = 1000, Hesaplanan=1.333333333327, Hata=6.49392e-12

Dosya Okuma ve Yazma

Programlarımızda tanımladığımız fonksiyonların argümanlarını kullanıcıdan raw_input() metoduyla soru sorarak veya komut satırından parametre olarak almak küçük boyutlu veriler için kullanışlıdır. Fakat büyük boyutlarda veri almak için genelde verileri bir dosyada saklayıp program içinden bu dosyanın içeriğini okuruz. Benzer şekilde program çıktıları büyük verilerden oluşuyorsa print yoluyla ekrana yazdırmak kullanışsızdır, bunun yerine çıktıyı yine bir dosyaya yazdırmak isteriz. Python'da dosya okuma ve yazma işlemlerini örnekler üzerinde açıklayacağız.


12.0
20.3
5.1
198.5
66.9
0.7
53.6
18.7
81.5
170.0

Yukarıdaki içeriğe sahip bir data1.txt dosyamız olsun. Görüldüğü gibi dosya içeriği on satırdan oluşmakta, her satır içeriği de bir ondalıklı sayı biçimindedir. Bu gibi veri içeren dosyaları salt metin editörlerinde oluşturmalıyız, MS Word gibi metin işleyicileriyle oluşturulan dosyalar fazladan veriler içerir.

Pythonda bir dosyanın içeriğini okumanın birden fazla yöntemi vardır, bunlardan birisi dosyanın içeriğini satır satır okumaktır. Dosya okuma işlemine başlamak için bir file (dosya) nesnesi oluşturmalıyız. Bunu dosya = open("data1.txt", "r") komutuyla yapabiliriz. Burada ilk argüman açılacak olan dosyanın diskteki yolunu, diğer argüman ise dosyanın hangi amaçla açılacağını belirtir. Burada kullandığımız r argümanı dosyayı okumak için (read) açtığımızı belirtiyoruz. Bu aşamadan sonra oluşturduğumuz dosya nesnesi üzerinde tanımlı olan dosya.readline() fonksiyonu ilk kez çağırıldığında dosyanın ilk satırının tamamı bir string verisi olarak okunur, bu fonksiyon bir kez daha çağırılınca sıradaki satır okunur. Dosya okuma işlemlerimiz bittikten sonra dosya nesnesini dosya.close() komutu ile kapatmalıyız.


dosya = open("data1.txt", "r")
s = dosya.readline()
print "satir 1: %s" % s
s = dosya.readline()
print "satir 2: %s" % s
dosya.close()

Yukarıdaki program dosyanın ilk iki satırını okuyup ekrana yazdırır, çıktısı aşağıdaki gibi olacaktır.


Terminal > python file1.py
satir 1: 12.0
satir 2: 20.3

Bu satırlarda bulunan sayılarla işlem yapmak için bunları float nesnelerine dönüştürmemiz gerekiyor.


dosya = open("data1.txt", "r")
s1 = float(dosya.readline())
print "satir 1: %s" % s1
s2 = float(dosya.readline())
print "satir 2: %s" % s2
dosya.close()

print "Toplam: %f" % (s1 + s2)

Bu programın çıktısı da aşağıdaki gibi olacaktır.


Terminal > python file2.py
satir 1: 12.0
satir 2: 20.3
Toplam: 32.300000

Dosya içindeki tüm sayıları toplamak için özel bir döngü kullanabiliriz.


dosya = open("data1.txt", "r")

toplam = 0
for s in dosya:
    toplam += float(s)

dosya.close()

print "toplam: %f" % toplam

Burada for döngüsünün kullanımına dikkat edelim. Döngü içinde dosya isimli nesne satırlardan oluşan bir list nesnesi olarak kabul edilir. Bu programın çıktısı aşağıdaki gibi olacaktır.


Terminal > python file3.py
toplam: 627.300000

Başka bir örnek olarak aşağıdaki programı inceleyin.


dosya = open("data1.txt", "r")

def y(t):
    return v0*t - 0.5*g*t**2

v0 = 1000; g = 9.81

for t in dosya:
    t = float(t)
    print "t=%5.1f, y(t)=%f" % (t, y(t))

dosya.close()

Bu program aşağıdaki çıktıyı verecektir.


Terminal > python file4.py
t= 12.0, y(t)=11293.680000
t= 20.3, y(t)=18278.698550
t=  5.1, y(t)=4972.420950
t=198.5, y(t)=5231.963750
t= 66.9, y(t)=44947.132950
t=  0.7, y(t)=697.596550
t= 53.6, y(t)=39508.131200
t= 18.7, y(t)=16984.770550
t= 81.5, y(t)=48919.763750
t=170.0, y(t)=28245.500000

Python'da dosya okumak için kullanılan başka bir komut da dosya.readlines() komutudur. Bu komut tüm dosyayı tek seferde okuyup her satırı bir string nesnesi olarak bir list nesnesine kaydeder.


>>> dosya = open("data1.txt", "r")
>>> s = dosya.readlines()
>>> print s
['12.0\n', '20.3\n', '5.1\n', '198.5\n', '66.9\n', '0.7\n', '53.6\n',
'18.7\n', '81.5\n', '170.0']
>>> x = float(s[3])
>>> print x
198.5
>>> dosya.close()

Yukarıdaki interaktif Python oturumunu incelerseniz her bir satır gizli olarak dosyada \n karakterini içeriyor, bu bir kaçış karakteridir ve nesneyi bir float nesnesine dönüştürmemizi engellemez. Dosya okumak için kullandığımız başka bir komut ise dosya.read() komutudur, bu komutla dosya içeriğinin tamamı tek seferde okunup tek bir string nesnesi olarak kaydedilir. Büyük boyutlu dosyalarda bu yöntem pek önerilmez. Daha sonra istersek bu string nesnesinin her satırını bir eleman olarak bir list nesnesine kaydedebiliriz, bunu yapmak için string nesneleri üzerinde tanımlanmış olan split() fonksiyonunu kullanırız.


>>> dosya = open("data1.txt", "r")
>>> s = dosya.read()
>>> type(s)
type 'str'>
>>> print s
12.0
20.3
5.1
198.5
66.9
0.7
53.6
18.7
81.5
170.0
>>> print s.split()
['12.0', '20.3', '5.1', '198.5', '66.9', '0.7', '53.6', '18.7',
'81.5', '170.0']
>>> dosya.close()

Python'da dosya açma ve kapama işlemini daha kompakt bir kod parçası ile yapabiliriz, bunun için with ifadesini kullanırız. Bir örnek üzerinde inceleyelim.


with open("data1.txt", "r") as dosya:
    toplam = 0
    for s in dosya:
        toplam += float(s)

print "toplam: %f" % toplam

Burada ilk satırdaki kodlar belirtilen isimdeki dosya nesnesini oluşturur, alt satırlardaki hizalı kodlar çalıştıktan sonra da dosya nesnesi otomatik olarak kapanır. Yukarıdaki örnekteki program ile aşağıdaki program bire bir aynı işi yapar, yani birbirine denktir. Python'da dosya işlemleri yapmanın önerilen yolu with ifadesini kullanmaktır.


dosya = open("data1.txt", "r")

toplam = 0
for s in dosya:
    toplam += float(s)

dosya.close()
print "toplam: %f" % toplam

Başka bir örnek olarak aşağıdaki dosyayı ele alalım.


Weather station ANKARA/CENTRAL is at about 40.00N 32.90E.
Height about 894m above sea level.
Jan  0.0
Feb  1.2
Mar  5.3
Apr  11.1
May  15.8
Jun  19.8
Jul  23.0
Aug  22.9
Sep  18.3
Oct  12.7
Nov  7.2
Dec  2.2
Year 11.6

Dikkat edilirse bu dosyanın satırlarında hem sayılar hem de metinler bulunmaktadır. Dosyanın içeriğine değinelim, bu dosya WorldClimate internet sitesindeki verilerden derlenmiştir. Ankara şehrinin 1926 ile 1991 yılları arasındaki 784 ay boyunca günlük ortalama hava sıcaklığı ölçülmüş ve aylara göre ortalama sıcaklık değerleri hesaplanmıştır. Bu dosyada her ayın ortamala sıcaklık değeri içerilmektedir. İlk iki satırda şehir ile ilgili kısa bir bilgi sunulmuş, dosyanın son satırında ise ölçümler sonucunda şehrin yıllık ortalama sıcaklık değeri sunulmuştur. Amacımız bu dosyadaki aylara ait sıcaklık verilerini bir listeye kaydetmek, liste ile daha sonra iletişim kurarak gerekli durumlarda erişebiliriz. Aşağıdaki programla bu işlemi gerçekleştirebiliriz.


with open("data2.txt", "r") as dosya:
    data = [x for x in dosya][2:-1] #gereksiz satirlari alma

aylar = []; sicakliklar = []
for satir in data:
    ay = satir.split()[0]           #satiri ayristir, ilk parca
    sicaklik = satir.split()[1]     #satiri ayristir, ikinci parca
    aylar.append(ay)
    sicakliklar.append(sicaklik)

for ay, sicaklik in zip(aylar, sicakliklar):
    print ay, sicaklik

Bu programın stratejisi söyledir. Önce dosyanın satırlarından bir liste oluşturuyoruz fakat ilk iki ve son satırı listede kaydetmiyoruz. Bu listenin her bir elemanı bir ay ve o aya ait sıcaklık bilgisinden oluşuyor. Daha sonra iki boş liste oluşturup bu listenin elemanlarını split() yöntemi ile parçalayıp olacak şekilde append() metodunu kullanan bir döngü kullanıyoruz. Son olarak bu listeleri bir tablo olarak yazdırıyoruz. Programın çıktısı aşağıdaki gibi olacaktır.


Terminal > python file7.py
Jan 0.0
Feb 1.2
Mar 5.3
Apr 11.1
May 15.8
Jun 19.8
Jul 23.0
Aug 22.9
Sep 18.3
Oct 12.7
Nov 7.2
Dec 2.2

Şimdi biraz da dosyaya veri yazma konusuna değineceğiz. Python'da dosyaya veri yazmak oldukça basittir, kullanılan fonksiyon dosya.write(s) komutudur. Bu komut dosya nesnesine s string nesnesini yazar. Dikkat edilmesi gereken kısım bu komutla yazılan string verisinin sonunda satır atlanmaz, dolayısıyla yeni satıra geçmek için yeni satır karakteri de eklenmelidir: dosya.write(s + '\n'). Ayrıca bu yöntemle bir dosyaya yeni baştan yazmak için dosya nesnesi dosya = open("data.txt", "w") biçiminde açılmalıdır, eğer dosyanın sonuna ekleme yapılmak isteniyorsa dosya = open("data.txt", "a") olarak açılmalıdır. Buradaki w ve a harfleri write ve append kelimelerini baş harfleridir. Burada bahsettiğimiz tüm yazma işlemleri için with ifadesi de kullanabilir.


y = lambda t: v0*t - 0.5*g*t**2
v0 = 5; g = 9.81
tlist = [i*0.15 for i in range(5)]

with open("data3.txt", "w") as dosya:
    for t in tlist:
        dosya.write("y(%4.2f)=%f\n" % (t, y(t)))

Bu programın çalıştırılması sonucunda oluşan data3.txt dosyasının içeriği aşağıdaki gibi olacaktır.


y(0.00)=0.000000
y(0.15)=0.639637
y(0.30)=1.058550
y(0.45)=1.256738
y(0.60)=1.234200

Hata Kontrolü

Daha önce yazdığımız aşağıdaki programı ele alalım.


import sys

t = sys.argv[1]
t = float(t); v0 = 2; g = 9.81
y = v0*t - 0.5*g*t**2
print y

Kullanıcı bu programı çalıştırırken bir sayıyı komut satırı argümanı olarak vermelidir. Eğer kullanıcı programı hiç bir argüman vermeden çalıştırırsa Python kullanıcıya bir hata mesajı gösterip programın çalışmasını durdurur.


Terminal > python sysargv1.py
Traceback (most recent call last):
File "sysargv1.py", line 3, in 
t = sys.argv[1]
IndexError: list index out of range

Hata mesajını incelersek sysargv1.py dosyasının 3. satırında t = sys.argv[1] kodlarında bir hata ile karşılaşıldığı belirtiliyor. Bu hatanın tipi IndexError, açıklaması da liste indisinin kapsam dışı olması olarak veriliyor. Python da program içinde oluşan kodlama hatalarına Exception denir. Gerçekten kodları inceleyecek olursak kullanıcı argüman girmediği için sys.argv listesinin tek bir elemanı var o da programın ismi, bunun da indisi 0 olacağından bu liste içinde 1 indisli hiç bir eleman yoktur. Programcı açısından bu açıklama yeterli olabilir ama kullanıcı için olası hataların programın içinde tespit edilip daha uygun bir açıklama ile uyarılması daha uygun olacaktır. Bu bölümde program içinde hataların tespit edilmesi konusu işlenecektir.

Örneğin yukarıdaki gibi IndexError hatalarını tespit etmek ve kullanıcıyı bilgilendirmek için aşağıdaki gibi bir yol izlenebilir.


import sys

if len(sys.argv) < 2:
    print "Bir arguman mutlaka girilmeli.."
    sys.exit(1)

t = float(sys.argv[1])
v0 = 2; g = 9.81
y = v0*t - 0.5*g*t**2
print y

Burada kullandığımız sys.exit() fonksiyonu programın o anda durdurulmasını sağlar. Argüman olarak 1 girerek karşılaşılan bir hata sonucu programın sonlandırıldığını belirtiyoruz, bunu 0 girersek hata ile karşılaşılmadan da programı sonlandırdığımızı belirtiriz. Bu programın çıktısı aşağıdaki gibi olacaktır.


Terminal > python except1.py 0.1
0.15095
Terminal > python except1.py
Bir arguman mutlaka girilmeli..

Python'da bu hata tespit ve kontrol yönteminin daha modern ve kompakt bir yolu vardır, bu işlemler için try-except bloklarını kullanırız. Bunu bir örnek üzerinde açıklayalım.


import sys

try:
    t = float(sys.argv[1])
except:
    print "Komut satirindan arguman alinamadi.."
    sys.exit(1)

v0 = 2; g = 9.81
y = v0*t - 0.5*g*t**2
print y

Görüldüğü gibi hata kontrolü için kullandığımız try-except yapısı, dallanma için kullandığımız if-else yapısına benzerdir. Burada program önce try bloğu içindeki kodları çalıştırmayı dener, eğer sorunsuz bir şekilde bu kodlar çalışırsa program except blokunu atlar ve 9 numaralı satırdan çalışmaya devam eder. Ama eğer try bloğu içinde verilen kodlar çalıştırılırken bir sorun ile karşılaşılırsa program except blokunda bulunan kodları çalıştırır.


Terminal > python except2.py 0.1
0.15095
Terminal > python except2.py
Komut satirindan arguman alinamadi..
Terminal > python except2.py 0.1saniye
Komut satirindan arguman alinamadi..

Yukarıda görülen program çıktısından da anlaşılacağı gibi sadece komut satırından argüman girilmemesi durumu söz konusu değildir. Kullanıcı komut satırından bir argüman girmiştir fakat bu veri float nesnesine çevrilemeyen bir veridir. Böyle durumlarda ValueError exception tipi oluşur.


Terminal > python sysargv1.py 0.1sn
Traceback (most recent call last):
File "sysargv1.py", line 4, in 
t = float(t); v0 = 2; g = 9.81
ValueError: invalid literal for float(): 0.1sn

Bunların dışında sık karşılaşılan exception tipleri şöyle sıralanabilir. NameError: tanımlanmamış bir değişken ismi ile karşılaşıldığında oluşur, ZeroDivisionError: sıfır ile bölme işlemi ile karşılaşılınca oluşur, TypeError: beraber işleme giremeyecek nesneler aynı işlemde bulunduğu zaman oluşur (örneğin string ile float çarpması yapmaya kalkışılırsa).

Yukarıdaki örnekte olduğu gibi tüm hata tipleri için tek bir genel durum oluşturmak çok iyi bir fikir değildir. Bunun yerine kullanıcıya nasıl bir hata yaptığını açıklayan bir mesaj iletebiliriz. Bunu yapmak için programda karşılaşılan hata tipini tespit edip buna göre mesaj yazdırmalıyız. Bunu except bloğunda hata çeşidini belirterek yapabiliriz.


import sys

try:
    t = float(sys.argv[1])
except IndexError:
    print "Komut satirindan bir arguman girilmeli.."
    sys.exit(1)
except ValueError:
    print "t=%s bir sayi degil.." % sys.argv[1]
    sys.exit(1)

v0 = 2; g = 9.81
y = v0*t - 0.5*g*t**2
print y

Yukarıdaki programın çıktısı aşağıdaki gibi olacaktır.


Terminal > python except3.py
Komut satirindan bir arguman girilmeli..
Terminal > python except3.py 0.1sn
t=0.1sn bir sayi degil..
Terminal > python except3.py 0.1
0.15095

Programda bir hata ile karşılaştığımızda ya bir hata mesajı yazdırıp sys.exit() ile programı sonlandırırız ya da bir exception üretiriz. İkinci işlem oldukça kolaydır ve raise E(mesaj) komutuyla yapılır. Burada E ile bir bilinen exception türü, mesaj ile de yazdırılacak mesajı belirtiriz.


import sys

try:
    t = float(sys.argv[1])
except IndexError:
    raise IndexError("Komut satirindan bir arguman girilmeli..")
except ValueError:
    raise ValueError("t=%s bir sayi degil.." % sys.argv[1])

v0 = 2; g = 9.81
y = v0*t - 0.5*g*t**2
print y

Bu yöntemle Python'da anlamlı olan fakat yapılan işlem içinde istemedğimiz sonuç üretecek durumlar için de kontrol sağlanabilir. Örneğin yukarıdaki örnekte $t$ aralığı $[0, 2v_0/g]$ biçiminde olmalıdır, aksi halde konum fiziksel olarak anlamsız olan negatif değerler olarak hesaplanır. Bu aralık dışında değerleri girilince raise ifadesi yardımıyla bir exception üretip programı durdurabiliriz.


import sys

try:
    t = float(sys.argv[1])
except IndexError:
    raise IndexError("Komut satirindan bir arguman girilmeli..")
except ValueError:
    raise ValueError("t=%s bir sayi degil.." % sys.argv[1])

v0 = 2; g = 9.81

if not 0 <= t <= 2*v0/g:
    raise ValueError("t=%g degeri [%g, %g] araliginda degil.." \
        % (t, 0, 2*v0/g))

y = v0*t - 0.5*g*t**2
print y

Bu programın çıktısı aşağıdaki gibi olacaktır.


Terminal > python except5.py 0.1sn
Traceback (most recent call last):
File "except5.py", line 8, in 
raise ValueError("t=%s bir sayi degil.." % sys.argv[1])
ValueError: t=0.1sn bir sayi degil..

Traceback (most recent call last):
File "except5.py", line 14, in 
% (t, 0, 2*v0/g))
ValueError: t=1 degeri [0, 0.407747] araliginda degil..

Burada hata kontrolü uygun biçimde yapılmış oldu ama tek bir sıkıntı var, hata mesajları kullanıcı açısından biraz karmaşık görünüyor. Kullanıcı kaynak kodları ve teknik bilgileri görmeden sadece uygun bir hata mesaji gösterilerek bilgilendirilebilir. Bunu yapmak için sadece kullanıcı girişini okuyan ve hata kontrolü yapan bir fonksiyon tanımlarız, daha sonra bunu ana programda tekrar try-except bloğundan geçiririz.


import sys
v0 = 2; g = 9.81

def veri_al():
    try:
        t = float(sys.argv[1])
    except IndexError:
        raise IndexError("Komut satirindan bir arguman girilmeli..")
    except ValueError:
        raise ValueError("t=%s bir sayi degil.." % sys.argv[1])
    if not 0 <= t <= 2*v0/g:
        raise ValueError("t=%g degeri [%g, %g] araliginda degil.." \
        % (t, 0, 2*v0/g))
    return t

try:
    t = veri_al()
except Exception as E:
    print E
    sys.exit(1)

y = v0*t - 0.5*g*t**2
print y

Python'da hata (exception) kontrolünün önerilen yöntemi bu son yazdığımız programda yaptığımız gibidir. Programın çıktısı aşağıdaki gibi olacaktır.


Terminal > python except6.py 1
t=1 degeri [0, 0.407747] araliginda degil..

Terminal > python except6.py 1sn
t=1sn bir sayi degil..

Terminal > python except6.py
Komut satirindan bir arguman girilmeli..

Terminal > python except6.py 0.1
0.15095
Önceki Ders Notu:
3.1. Python Fonksiyonları
Dersin Ana Sayfası:
Python ve Bilimsel Hesaplama
Sonraki Ders Notu:
3.3. Python'da Modüller