Python Programlama Dili

Bölüm 2

Genel Yöntemler

Top Navigasyon

Ders 15

2.3 - Fonksiyonların Uygulanması (Devam)

2.3.4 - Anahtar Değerli Argümanlar

Bir laboratuvarda deney yaptığınızı düşünün. Her deneyde, bir cihazdan okumalar yapıyor ve okuduğumuz kadar noktanın ortalamasını alıyoruz. Ortalama için en az iki değere gereksinme olduğu için, her deneyde en az iki okuma yapmalıyız. Daha fazla okuma doğal olarak daha iyi olur, ama çeşitli nedenleden dolayı, yeterince okuma yapamıyoruz. Her deneyde, sayısı belli olmayan okumalar yapabiliyoruz.

Bu deneyde okumalar 'A' = 12.34 ,'B' = 13.08 gibi, bir isim, bir değer şeklinde yapılıyor. Bir okuma() fonksiyonu yaratıp okunan değerlerin ortalamasını almak istiyoruz. Bu fonksiyon, def okuma('A' = 12.34 , 'B' = 13.08): olarak tanıtılabilir. Fazla okumalar için, Python programlama dilinin anahtar sözcüklerle tanıtılan argümanlar olanağından yararlanabiliriz. Aşağıdaki program, bu tip bir uygulamayı açıklamaktadır.

Ders15program1.py
    
from test.test_configparser import SortedDict;

def okuma(A = 23.5 , B = 23.8, ** ötekiler):
    
ötekiler=SortedDict(ötekiler);
    
print(list(ötekiler.keys()));
    
print(A, '  ', B , '  ' , end = '');

toplam = A + B;
 
for k in ötekiler.keys():
toplam = toplam + ötekiler[k];

print(ötekiler[k] , '  ', end = '');
        
g = len(ötekiler);
    
print('ortalama = ', toplam/(g+2));

okuma(22.2 , 22.4 , C = 22.6 , D = 22.4);
          
---------Program----Sonucunun----Görüntülenmesi-----------------------
['C', 'D']
22.2 22.4 22.6 22.4 ortalama = 22.4

Yukarıdaki programda, açıklanacak çok nokta bulunmaktadır. Herşeyden önce, program verilerinden ilk ikisi zorunlu verilerdir ve bu yüzden formal parametre listesinde isimleri açıkça belirtilmilştir İki zorunlu veri olmasının nedeni, okumaların ortalaması alınırken, en az iki okuma değerinin gerekli olmasıdır. Diğer veriler gerekli değildir, fakat ortalama ne kadar çok veri noktası ile alınırsa, o kadar çok gerçeğe (kayma dışında) yakın olur. Bu nedenle olanaklar ölçüsünde çok nokta ile okuma yapıldığında, ortalamanın alabilmesi için, ikiden sonraki nokta sayısı, her deneye uyabilecek şekilde değişebilecek sayıda değer çiftleri olacak şekilde programlanmıştır.

Yukarıdaki programda, okuma() fonksiyonunun **ötekiler olarak belirtilen argümanında tanımlanan veriler, Python programlama dilinde, "Sözlük" olarak tanımlanan bir veri tipindedir.

Python programlama dilinde, sözlük tipi veriler, {anahtar:değer , anahtar:değer, ...} şeklinde tanımlanan veri tipini oluşturur. Bu veri tipinde anahtar değerleri sözel değerler (string), topluluk (tuple) gibi, değiştirilemeyen (immutable) veri tipinde olmalıdır. Değer ise, istenen veri tipinde olabilir. Python programlama dilinde, sözlük tipi veriler, JavaScript programlama dilinde, ilişkisel diziler olarak, tanımlanır.

Örneğimizde, ötekiler adındaki, formal parametre değerlerine istenilen sıra ile C = okunan değer, D = okunan değer, şeklinde istenildiği kadar veri çifti şekilnde gerçek argümanlar tanımlanabilir.

Python derleyicisi, sözlük verilerini anahtar değerlerine göre sıralı olarak değil gelişigüzel olarak değerlendirir. Bu nedenle, insanlara göre daha anlamlı sonuçların alınması için, öncelikle girilmiş olan sözlük verilerinin anahtar değerlerine göre sıralanması uygun olur. Bu sıralama Python programlama dilinin hazır sortedDict() fonksiyonunun programa ithal edilmesi ile sağlanır. Bu ithal, program kodlarının yazıldığı Eclipse platformunun pyDev eklentisinin, programın yükünü arttırmayacak şekilde düzenlemesi ile gerçekleştirilmiştir.

Programa girilmiş olan veri noktalarının belirtilmesi için, sözlük değerlerinin anahtar değerlerine erişim yapmak gereklidir. Bu erişim, önce sözlük verilerinin anahtar değerlerinin bir listede toplanması ve bu listenin görüntülenmesi ile sağlanmıştır.

Ortalamanın alınması için, sözlük verilerinin değerlerine erişmek gereklidir. Bu erişim, bir döngü yapısı ile gerçekleştirilmiştir. Döngü, sözlük verilerinin, anahtar değerlerini toplayan liste elemanlarının taranması ile gerçekleştirimiş, her liste elemanına karşı gelen sözlük değeri, bir akümülatörde toplanarak, önce toplam değeri, sonra da ortalaması alınmıştır.

Yukarıdaki program, veri noktalarının görüntülenmsi için, detaylı olarak yazılmıştır. Salt ortalamanın görüntülenmesi için, daha kısa bir program da yeterli olabilir.

Python programlama dilinin sözlük (dict) tipi verilerinin anahtar (keyword) değerleri argüman olarak alan fonksiyonların argümanlarına kwargs (keyword arguments) adı verilir. Bunlar,

def fnFoo(**kwargs):
    

olarak tanımlanırlar.Burada kwargs, sözlük anahtarlarıdır. Formal parametre olarak kwargs tanımlanır, gerçek argüman olarak, tanımlanmış anahtar isimlerinin değerleri fonksiyon tarafından bulunur ve kullanılır. Bunun bir uygulaması yukarıdaki programda görülmüştü. İkinci bir örnek olarak, aşağıdaki program yine böyle bir uygulamayı içermektedir.

# Ders15program2.py

sözlük = { 'x': 1 , 'y': 2 , 'z':16};

def topla(kwargs):
s = 0;
for i in kwargs:
s = s + kwargs[i];
return s;

q = topla(**sözlük);

print('Toplam = ' , q);
    
---------Program----Sonucunun----Görüntülenmesi-----------------------
Toplam = 19

Yukarıdaki programdan görüldüğü gibi, istenilen sayıda eleman içeren sözlük verilerinin, her anahtar değerine karşı gelen değerler üzerinde her türlü işlemin yapılabilmesinin olanağı bulunmaktadır.

2.3.5 - Generator Fonksiyonlar

Python programlama dilinde, Javascript ve Java gibi dillerde alışılagelmiş for/next döngülerinin bulunmadığı görülmüştü. Buna rağmen, buna benzer bir sekansı kendi generator fonksiyonlarımızla oluşturabiliriz.

Generator fonksiyonları, kendi iteratorlarımızı tanımlayıp uygulamamıza yardımcı olur. Bu fonksiyonlar, içlerinde bir while döngüsü taşırlar. Bu, onları belirli sayıda döngü gerçekleştirerek, sonsuz döngüye girmelerini engeler. Bu konuda bir örnek, aşağıdaki programda verilmiştir.

# Ders15program3.py

def generator1 (n = 0):
i = s = 0;
while i < n :
s += i ** 2;
yield s;
i += 1;
return s;

k = 0;

for i in generator1(5):
print ("k =", k , 'i = ', i);
k += 1;

---------Program----Sonucunun----Görüntülenmesi-----------------------
k = 0 i = 0
k = 1 i = 1
k = 2 i = 5
k = 3 i = 14
k = 4 i = 30

Yukarıdaki program, ilk n tamsayılarının karelerinin toplamını hesaplamaktadır.

2.3.6 - Dekorator Fonksiyonlar

Dekorator fonksiyonlar, bir üst fonksiyonu, bir iç fonksiyona bağlar. İlk önce aşağıdaki dış fonksiyonu gözönüne alalım.

# Ders15program4

def dışFonksiyon(fonksiyon):
print("fonksiyonçağrısı :" , fonksiyon.__name__);
def içFonksiyon(*argümanlar):
print("çalıştırılıyor : ", fonksiyon.__name__);
return fonksiyon(*argümanlar);
return içFonksiyon;

def benim_fonksiyonum(parametre): print(parametre);
benim_fonksiyonum = dışFonksiyon(benim_fonksiyonum);

benim_fonksiyonum("Günaydın") ---------Program----Sonucunun----Görüntülenmesi-----------------------
fonksiyonçağrısı : benim_fonksiyonum
çalıştırılıyor : benim_fonksiyonum
Günaydın

Yukarıdaki fonksiyonun yürüşü klasik Pyhthon programlama yöntemlerinin uygulanması ile gerçekleşir ve bir dış fonksiyon bir iç fonksiyonu döndürür.

Aşağıdaki program ise, aynı işlevi bir decorator bildirimi ile gerçekleştirmektedir.

# Ders15program5

def dışFonksiyon(fonksiyon):
print("fonksiyonçağrısı :" , fonksiyon.__name__);
def içFonksiyon(*argümanlar):
print("çalıştırılıyor : ", fonksiyon.__name__);
return fonksiyon(*argümanlar);
return içFonksiyon;


@dışFonksiyon

def benim_fonksiyonum(parametre): print(parametre);


benim_fonksiyonum("Günaydın") ---------Program----Sonucunun----Görüntülenmesi-----------------------
fonksiyonçağrısı : benim_fonksiyonum
çalıştırılıyor : benim_fonksiyonum
Günaydın

Görüldüğü gibi, yukarıdaki programda, bağlama işlemi daha kısa bir kodla bir decorator uygulaması ile geçekleştirilmiş olmaktadır.

Daha önce ** ile açıklanan istenilen sayıda sözlük veri tipinin anahtar değerlerini argüman olarak alan fonksiyonları incelemiştik.

2.3.7 - Fonksiyon içinde Değer Değişimi

Bu konuda, daha önce birçok uygulama yapmış ve temel bilgileri almış durumdayız. Fakat, bu kısımda, bilgilerimizi hem tekrar etmek, hem de -özellikle yeni başlayanlar için- daha netleştirmenin faydalı olacağı kuşkusuzdur.

Herşeyden önce, bir değişkenin değerine erişim, bir değişkenin değerinin güncellenmesi, bir değişkene yeni değer atanması tamamen farklı olaylardır ve aralarındaki farkın iyice özümsenmesi gerekmektedir.

Bir değişken değerine erişim, bu değeri kullanma ve değiştirme hakkına sahip olunması anlamına gelir.

Global bir değişkene tüm alt programlardan erişilebilir. Örnek:

global1 = 999;

def fn1():
   
print(global1); #erişim

fn1();
Sonuç:
999

Bir alt programdan, global bir değişkene, değerini yazdırarak erişiliyor. Erişim, değerin kullanılması anlamına gelir. Bir alt programdan, global değişkenlere erişim ve güncelleme olanağı sağlanmıştır. Burada global değişkenin veri tipi, kısmen güncellenemeyen (immutable) bir veri tipi olan sayısal veri tipi olduğundan, sadece erişim hakkı bulunmaktadır.

Global değişkeni, bir alt programa argüman olarak geçmek de olasıdır. Bu durumda, global değişkene hem kendi adı ile hem de argüman adı ile erişim olanağı bulunabilir. Örnek:

global1 = 999;

def fn1(x):
x = 16;
print("Fonksiyon içinde :" , x);
print("Fonksiyon içinde :" , global1);  # erişim

fn1(global1);

print("Fonksiyon uygulandıktan sonra : " , global1);  # erişim

Sonuç:
Fonksiyon içinde : 16
Fonksiyon içinde : 999
Fonksiyon Uygulandıktan Sonra : 999

Görüldüğü gibi, global alanda tanımlı bir global değişken, bir alt fonksiyona argüman olarak uygulanıyor. Fonksiyon içinde argüman değeri değişiyor, fakat bu değer değişmesi, fonksiyon dışına yansımıyor. Bunun nedeni, argüman olarak aktarılan değerin, gerçek verinin sadece bir kopyası olmasıdır. Bu durumda, fonksiyon içindeki değer değişimi, sadece koyanın değerini değiştiriyor ve bundan dolayı, değer değişimi, sadece fonksiyon içinde geçerli oluyor ve fonksiyon dışına yansımıyor.

Fonksiyon içinde, global <global değişken> bildirimi yapılırsa, durum değişir bu durumda, global değişkenin değerinde, fonksiyon içinde yapılan her değişiklik, global değişkenin dış değerine de yansır. Fakat burada da çok dikkatli olmak gerekir. Örnek:

global1 = 999;

def fn1(x):
global global1;
x = 16;
print("Fonksiyon içinde :" , x);
print("Fonksiyon içinde :" , global1);  # erişim

fn1(global1);

print("Fonksiyon uygulandıktan sonra : " , global1);  # erişim
Sonuç:
Fonksiyon içinde : 16
Fonksiyon içinde : 999
Fonksiyon Uygulandıktan Sonra : 999

Yukarıdaki fonksiyonda, bir global değişken, bir alt programda argüman olarak aktarılmış ve argüma olarak alt program içinde değeri değiştirilmiş olmasına karşın, bu değişim, global değişkenin değerine yansımamamıştır. Bunun nedeni, argüman olarak atanan değerin, esas değer değil bunun bir kopyası olmasıdır. Bundan sonra, global global1 bildirimi yapılmış ve ancak bu bildirimden sonra alt programda global değişken üzerinde yapılan değişiklik, global değişkenin değerini fonksiyon dışında da değiştirmiştir.

Bir de kısmen değiştirilebilir (mutable verilerin davranışı üzerinde duralım.

global1 = [1 , 2 , 3];

print("Fonksiyon uygulanmadan önce : " , global1);  # erişim

def fn1(x):
    
x = [1 , 2 , 3 , 4];

print("Fonksiyon içinde :" , x);
    
print("Fonksiyon içinde :" , global1);  # erişim

fn1(global1);

print("Fonksiyon uygulandıktan sonra : " , global1);  # erişim
Sonuç:
Fonksiyon Uygulanmadan Önce : [1, 2, 3]
Fonksiyon içinde : [1, 2, 3, 4]
Fonksiyon içinde : [1, 2, 3]
Foksiyon Uygulandıktan Sonra : [1, 2, 3]

Yukarıdaki uygulama incelendiğinde, değiştirilebilir (mutable) tipte bir verinin, bir alt programa, argüman olarak aktarıldığını, fonksiyon içinde argümanın değerinin yenilendiğini, fakat bu değer değişiminin, global değişkenin ne fonksiyhon içindeki ne de fonksiyon dışındaki değerini etkilemediği görülmektedir.

Bir global değişkene, bir fonksiyon içinde yeni bir değer atandığında, bunun fonksiyon dışındaki değerini etkileyip etkilemediğini kontrol edelim.

global1 = [1 , 2 , 3];

print("Fonksiyon uygulanmadan önce : " , global1);  # erişim

def fn1() :
    
global1 = [1 , 2 , 3 , 4]; # Sadece yeni bir bellek adresi açılıyor (Yerel değişken)
    
print("Fonksiyon içinde :" , global1);  # Sadece yerel değişkene erişim

fn1();

print("Fonksiyon uygulandıktan sonra : " , global1);  # erişim
Sonuç:
Fonksiyon Uygulanmadan Önce : [1, 2, 3]
Fonksiyon içinde : [1, 2, 3, 4]
Foksiyon Uygulandıktan Sonra : [1, 2, 3]

Burada, alt program içinde global1 adlı değişken tanımlanıp yeni bir değer ataması yapılıyor. Bu işlem, asla kısmen güncellenebilir bir değişken tipi olan liste tipindeki global değişkenin değerine ekleme yapıldığı anlamına gelmez. Aksine, burada kapsam alanı fonksiyon içi ile sınırlı bir yerel değişken tanımlandığı anlamına gelir. Yerel değişkene, global değişken ile aynı adın verilmesi bir talihsizlikten den de öte, bir densizlik olayı sayılabilir. Bu, Python derleyicisini de yoran, programın sonradan bakım için, okunmasında da sorun yaratan bir eylemdir. En iyisi, yerel ve global değişkenlerinin isimlerinin çakışmamasının sağlanmasıdır.

Eğer alt program içinde,  global global1 bildirimi yapılmış ise, alt programda global değişken1 üzerinde yapılan değişiklik fonksiyon dışındaki değeri de etkiler. Örnek,

global1 = [1 , 2 , 3];

print("Fonksiyon uygulanmadan önce : " , global1);  # erişim

def fn1() :

global global1;
    
global1 = [1 , 2 , 3 , 4]; # global değişkenin değeri değişiyor.
    
print("Fonksiyon içinde :" , global1);  # Sadece yerel değişkene erişim

fn1();

print("Fonksiyon uygulandıktan sonra : " , global1);  # erişim
Sonuç:
Fonksiyon Uygulanmadan Önce : [1, 2, 3]
Fonksiyon içinde : [1, 2, 3, 4]
Fonksiyon Uygulandıktan Sonra : [1, 2, 3, 4]

Kısmen değiştirilebilir (mutable) bir verinin, bir alt programda güncelenebileceği tek durum, aşağıda görülmektedir.

global1 = [1 , 2 , 3];

print("Fonksiyon uygulanmadan önce : " , global1);  # erişim

def fn1() :

global1.add(4);# global değişken güncelleniyor.

    
    
print("Fonksiyon içinde :" , global1);  # erişim

fn1();

print("Fonksiyon uygulandıktan sonra : " , global1);  # erişim
Sonuç:
Fonksiyon Uygulanmadan Önce : [1, 2, 3]
Fonksiyon içinde : [1, 2, 3, 4]
Fonksiyon Uygulandıktan Sonra : [1, 2, 3, 4]