İç-içə funksiyalar

Python proqramlaşdırma dilində iç-içə funksiyalardan istifadə oluna bilər. Başqa sözlə, bir funksiya daxilində digər funksiyaları təyin edib çağırmaq mümkündür. Gəlin bunu təcrübədə nəzərdən keçirək.
Aşağıdakı proqramda daxil edilən ədədlərin ədədi ortasını tapmaq üçün iç-içə iki funksiyadan istifadə olunur:

>>> def avg(*args):
        def gav(sval, count):
            return sval / count 
        
        n = len(args)
        s = 0
        for i in args:
            s += i
        return gav(s, n)

>>> avg(1, 4, 7) 
4.0
>>>

Proqram mətnini oxuyarkən görəcəksiniz ki, əvvəlcə avg() adlı funksiya elan olunur. Bu funksiyanın daxilində isə gav() funksiyası təyin olunur. Proqram icra olunarkən avg() funksiyası çağrılaraq ona üç arqument (1, 4, 7) ötürülür. Bu arqumentləri qəbul edən avg() funksiyası onların sayını (n) və cəmini (s) hesablayır və qiymət kimi gav() funksiyasını qaytarır. Başqa sözlə, avg() funksiyası gav() funksiyasını çağıraraq öz işini yekunlaşdırır. Öz növbəsində gav() funksiyası ona ötürülən arqumentləri (s, n) qəbul edir və qiymət olaraq onların nisbətini qaytarır. Nəticədə daxil edilən ədədlərin ədədi ortası (4.0) əldə edilmiş olur. Bu qədər sadə.
Ümumiyyətlə, istənilən qədər iç-içə funksiyalardan istifadə oluna bilər. Burada bir məqama diqqət yetirmək lazımdır ki, daxili funksiya yalnız elan olunduğu yerdən çağırılmalıdır. Misal üçün: yuxarıda yazdığımız proqram kodunda gav() funksiyası daxili funksiya olduğuna görə onu yalnız avg() funksiyası daxilində çağırıb icra etmək mümkündür. Əsas proqramdan gav() funksiyası çağırıldığı zaman interpretator xəta qeydə alacaq.

İndi də başqa bir misalı nəzərdən keçirək. Skriptdə yazılmış aşağıdakı proqramda ədədlər üzərində sadə riyazi əməllər aparmaq üçün üç müstəqil funksiya təyin olunur. Bunlardan calc() funksiyası əsas funksiya rolunda çıxış edir və digər iki funksiya onun daxilindən çağırılır. Gördüyünüz kimi bir funksiya daxilində onun öz xaricində təyin olunan başqa funksiyaları da çağırmaq olar.

def input_nums():
    a = float(input("Birinci ədədi daxil edin: "))
    b = float(input("İkinci ədədi daxil edin: "))
    return a, b

def input_oper():
    print("---------------")
    print("| + : Toplama |")
    print("| - : Çıxma   |")
    print("| * : Vurma   |")
    print("| / : Bölmə   |")
    print("| x : Çıxış   |")
    print("---------------")
    t = input("Seçiminizi edin: ").lower()

    while t not in ['+', '-', '*', '/', 'x']:
        t = input("Seçiminizi edin: ").lower()

    return t

def calc():
    n1, n2 = input_nums()
    p = input_oper()

    if p == "+":
        print("Cəm:", n1 + n2)
    elif p == "-":
        print("Fərq:", n1 - n2)
    elif p == "*":
        print("Hasil:", n1 * n2)
    elif p == "/":
        print("Qismət:", n1 / n2)
    else:
        print("Proqramdan çıxdınız.")


calc()

Proqramı yazıb icra edin və hər dəfə müxtəlif ədədlər və əməliyyat növü daxil edərək nəticələri yoxlayın.

Biz proqram daxilində elan olunan qlobal dəyişənlərdən funksiyalarda istifadə edilməsini öyrənmişdik. İç-içə funksiyalarda da üst (xarici) funksiyada təyin olunan dəyişənlərdən alt (daxili) funksiyalarda istifadə etmək mümkündür:

>>> def outer(): 
        def inner():
            z = 3 
            print("inner:", z) 
            print("outer:", y) 
            print("global:", x)

        y = 2 
        inner()

>>> x = 1 
>>> outer() 
inner: 3 
outer: 2 
global: 1 
>>>

Gördüyünüz kimi, bu proqramda outer(), onun daxilində isə inner() funksiyası təyin edilmişdir. Şərti olaraq, daxili funksiya kimi qəbul etdiyimiz inner() funksiyasında elan edilən outer() funksiyasının y və əsas proqramın x dəyişənlərindən istifadə olunur.

Lokal və qlobal dəyişənlər

Funksiyalarda parametrlərdən əlavə dəyişənlərdən də istifadə oluna bilər. Funksiya daxilində elan olunan dəyişənlər lokal, proqram daxilində elan olunanlar isə qlobal dəyişənlər adlanır. Dəyişənlərin elan olunduqları yerdən asılı olaraq fərqli təsir sahələri vardır. Lokal dəyişənlər yalnız elan olunduqları funksiya daxilində işləyir. Qlobal dəyişənlər isə həm elan olunduqları proqramda, həm də bu proqram daxilində təyin olunan bütün funksiyalarda keçərlidir.
Lokal və qlobal dəyişənlərdən istifadəni təcrübədə nəzərdən keçirək. Skriptdə yazılan aşağıdakı proqramda üç şagirdin informatikadan aldığı qiymətlər daxil edilir və iki ayrı funksiyada orta və yüksək qiymətlər tapılır:

def avgval(pts): 
    s = 0
    for i in pts: 
        s += pts[i]
    return s / n

def maxval(pts): 
    max = m
    for i in pts:
        if pts[i] > max:
            max = pts[i] 
    return max

a = int(input('Əlinin qiyməti: '))
b = int(input('Vəlinin qiyməti: '))
c = int(input('Pirvəlinin qiyməti: ')) 
std = {'t1':a, 't2':b, 't3':c} 
n = len(std)
m = 0
print('Orta qiymət:', avgval(std)) 
print('Yüksək qiymət:', maxval(std))

Proqramı araşdırsanız, görəcəksiniz ki, biz burada bir sıra qlobal dəyişənlərdən istifadə etdik. Onlardan n və m müvafiq olaraq avgval() və maxval() funksiyaları daxilində də istifadə olundu. Təyin olunan funksiyaların içərisində isə müxtəlif lokal dəyişənlər elan olundu. Bir məqama da diqqət yetirin ki, hər iki funksiyada eyniadlı i lokal dəyişənindən istifadə olunur. Bu tamamilə yol veriləndir, çünki fərqli yerlərdə elan olunduqlarına görə onlar mahiyyətcə ayrı-ayrı dəyişənlərdir. Yeri gəlmişkən, fərqli funksiyaların başlıqlarındakı parametrlər də eyniadlı ola bilər.
Onu da deyək ki, funksiyalara məlumatötürmə yolu yalnız arqumentlərdən istifadə etməkdən ibarət deyil. Gördüyünüz kimi, qlobal dəyişənlər vasitəsilə də bunu etmək mümkündür. Daha bir neçə misalı nəzərdən keçirək:

>>> def hscore(): 
        print(s)

>>> s = 5
>>> hscore() 
5
>>>

Bu proqramda s qlobal dəyişənindən funksiya daxilində istifadə olunur. Onu da nəzərə almaq lazımdır ki, funksiyanın, ümumiyyətlə, parametri yoxdur və o, kənardan yalnız qlobal dəyişən vasitəsilə məlumat qəbul edə bilir.
Gəlin belə bir eksperiment aparaq: funksiya daxilində eyniadlı s dəyişəni elan edək və ona fərqli qiymət mənimsədək. Sizcə, nə baş verəcək?

>>> def hscore(): 
            s = 10
            print(s)

>>> s = 5
>>> hscore()
10
>>> print('Qlobal dəyişənin qiyməti:', s) 
Qlobal dəyişənin qiyməti: 5
>>>

Gördüyünüz kimi, s qlobal dəyişəninin qiyməti dəyişməz qaldı. Funksiya daxilindəki eyniadlı s dəyişəni isə lokal dəyişən kimi təyin edildi və 10 qiyməti də qlobal dəyişənə deyil, məhz ona mənimsədildi.
Əgər funksiya daxilində qlobal dəyişənin qiymətini dəyişmək tələb olunursa, bunu etməzdən qabaq dəyişənin önünə global açar sözünü yazmaq lazımdır. Bununla interpretator həmin dəyişənin qlobal dəyişən olduğunu anlayacaq:

>>> def hscore(): 
        global s
        s = 10 
        print(s)

>>> s = 5
>>> hscore()
10
>>> print('Qlobal dəyişənin qiyməti:', s) 
Qlobal dəyişənin qiyməti: 10
>>>

Qaytarılan qiymətlər

Yuxarıda qeyd etmişdik ki, funksiyalar arqumentləri qəbul edən və qiymət qaytaran obyektlərdir. Misallardan da gördüyünüz kimi, funksiya öz icrası nəticəsində əldə olunan qiyməti return operatoru vasitəsilə qaytarır. Bu operatordan istifadə edilməsi nəticəsində funksiya həm də öz işini yekunlaşdıraraq idarəetməni çağrıldığı yerə ötürür və proqramın icrası qaldığı yerdən davam edir.
Gəlin bir neçə misalı nəzərdən keçirək:

>>> def absum(a, b): 
        s = 0
        for i in range(a, b + 1): 
            s += i
        return s

>>> print(absum(1, 10)) 
55
>>>

Yuxarıdakı proqramda verilmiş aralıqda ədədlərin cəmini hesablayan funksiya təyin edilir. Funksiya daxilində hesablamanın nəticəsi (s) funksiyanın qiyməti olaraq return ilə qaytarılır. Daha sonra proqram daxilində biz bu funksiyaya print(absum(1,10)) ifadəsi daxilində müraciət edirik. Bunun nəticəsində absum() funksiyasının qaytardığı qiymət ekranda çapa verilir.
Funksiya daxilində return operatorundan sonra nəticəsi hesablana bilən ifadə də yer ala bilər:

>>> def sum(a, b): 
        return a + b

>>> x = 1234
>>> y = 4321
>>> sum(x, y)
5555
>>>

Funksiyanın hər hansı bir qiymət qaytarmasını istəmirsinizsə, return açar sözündən sonra None yazmalı və ya ümumiyyətlə, return operatorundan istifadə etməməlisiniz. Bu zaman hər iki halda funksiya qiymət olaraq None (heçnə) qaytaracaq:

>>> def SayHello(): 
        print('Salamlar')
        return None

>>> print(SayHello()) 
Salamlar
None
>>>

Yuxarıdakı proqramda SayHello() funksiyasını print() ilə çap etmək istədikdə nə baş verdiyinə diqqət yetirin. Ekrana birinci sətirdə Salamlar, ikinci sətirdə isə None çıxır. Burada birinci sətirdə çıxan ifadə SayHello() funksiyasının daxilində icra olunan print(‘Salamlar’) əməliyyatının nəticəsidir. İkinci sətirdəki None ifadəsi isə SayHello() funksiyasının qaytardığı qiymətdir.
Aşağıdakı proqramda daxil edilən sözün hərflərini ayrı-ayrı sətirlərdə yazan funksiya düzəldilir. Bu funksiyanı təyin edərkən, ümumiyyətlə, return operatorundan istifadə olunmur:

>>> def wordlet(s): 
        for i in s:
            print(i)

>>> wordlet('savalan') 
s
a
v
a
l
a
n 
>>>

Belə bir sual versəm, yəqin ki, sizin üçün də maraqlı olar: görəsən, funksiya birdən çox qiymət qaytara bilərmi? Əslində funksiya hər zaman yalnız bir qiymət, daha dəqiq desək, bir obyekt qaytarır. Lakin funksiyanın qaytardığı bu obyekt istənilən tipdə, o cümlədən kortej, siyahı və lüğət kimi konteyner verilənlər tipində də ola bilər. Bu isə o deməkdir ki, funksiyanın qaytardığı obyekti qiymətlər toplusu kimi verməklə dolayı yolla birdən çox qiymət qaytara bilərik. Adətən, funksiya bu yolla birdən çox qiyməti kortej şəklində qaytarır. Bir misal üzərində bunu nəzərdən keçirək.
Aşağıdakı proqramda verilmiş iki ədədi bir-birinə bölüb tam və qalıq hissəni əldə edən funksiya düzəldilir. Bu funksiya bir cüt qiymət (tam və qalıq) qaytarır:

>>> def adimob(a, b): 
        return a // b, a % b

>>> adimob(5, 2)
(2, 1)
>>> type(adimob(5, 2)) 
<class 'tuple'>
>>> x, y = adimob(5, 2)
>>> x 
2
>>> y 
1
>>>

Gördüyünüz kimi, adimob() funksiyası kortej şəklində iki qiymət qaytarır.