staticmethod və classmethod

Mövzumuzun əvvəlində göstərdik ki, siniflər elan olunarkən sahələr (dəyişənlər) ilə yanaşı metodlar (funksiyalar) da təyin edilir. Bu metodlardan daha sonra sinfin nümayəndələri vasitəsilə çağırılaraq istifadə olunur. Bundan başqa, sinfin özü üçün təyin edilən metodlar da vardır. Bunlar staticmethod və classmethod kimi təyin olunan metodlardır.

staticmethod

Metod staticmethod kimi təyin edilirsə, bu zaman self parametri verilmir:

class Personal:
    count = 0

    def __init__(self, name, pos):
        self.name = name
        self.pos = pos
        print("{0} {1} vəzifəsinə qəbul edildi".
              format(self.name, self.pos))
        Personal.count += 1

    def pers_num():
        print("Personal sayı: {0}".format(Personal.count))

    pers_num = staticmethod(pers_num)

p1 = Personal("Kərim Tahiroğlu", "müdir")
Personal.pers_num()
p2 = Personal("Murad Talıblı", "menecer")
Personal.pers_num()

Yuxarıdakı proqram kodunda biz sinif daxilində pers_num() adlı metodu təyin edib onu staticmethod edirik. Bundan sonra bu metod sinfin özü vasitəsilə çağırılır.
Metodları staticmethod kimi təyin etmək üçün dekoratordan da istifadə edilə bilər:

@staticmethod 
def pers_num():
    print("Personal sayı: {0}".format(Personal.count))

classmethod

Siniflər üçün metodlar classmethod kimi də təyin oluna bilər. Bu zaman funksiyanın ilk parametri cls adlı parametr olur. Sinif bu metodu çağırarkən cls parametri onun özünü bildirir:

class Personal: 
    count = 0

    def __init__(self, name, pos):
        self.name = name
        self.pos = pos
        print("{0} {1} vəzifəsinə qəbul edildi".
              format(self.name, self.pos))
        Personal.count += 1

    def pers_num(cls):
        print("Personal sayı: {0}".format(Personal.count))

    pers_num = classmethod(pers_num)

p1 = Personal("Kərim Tahiroğlu", "müdir")
Personal.pers_num()
p2 = Personal("Murad Talıblı", "menecer")
Personal.pers_num()

Yuxarıdakı proqram kodunda biz sinif daxilində pers_num() adlı metodu müəyyənləşdirib onu classmethod edirik. Bundan sonra bu metod sinfin özü vasitəsilə çağırılır.
Metodları classmethod kimi müəyyənləşdirmək üçün dekoratordan da istifadə edilə bilər:

@classmethod
def pers_num(cls):
    print("Personal sayı: {0}".format(Personal.count))

Fayl metodları

Python proqramlaşdırma dilində fayl obyektləri ilə işləmək üçün bir sıra metodlar nəzərdə tutulmuşdur. Gəlin bu metodları bir-bir nəzərdən keçirək:

f.write(sətir)

Bu metod verilmiş sətri fayla yazır və simvolların sayını qaytarır:

>>> f = open('adlar.txt', 'w')
>>> f.write('Anar\n') 
5
>>> f.write('Rasim\n')
6
>>> f.write('Firudin\n') 
8
>>>

Biz burada hər sətrin sonuna \n simvollarını (sətir sonu) əlavə etməklə kursoru növbəti sətrə keçirmiş oluruq. Verilmiş sətirlərdə simvolların sayında bu sətir sonu simvolları da nəzərə alınır.
Fayla sətirdən fərqli bir məlumat yazmaq istəyirsinizsə, öncədən onu sətir (str) tipinə çevirməlisiniz:

>>> f = open('adlar.txt', 'w')
>>> name_age = ('Nadir', 33)
>>> s = str(name_age)
>>> f.write(s)
13 
>>>

Burada biz kortej tipdə məlumatı sətir tipinə çevirib fayla yazdıq.

f.close()

Diqqət etdinizsə, bayaqdan fayla yazdığımız məlumatlar faylda yadda saxlanmadı. Bunun səbəbi sonda faylı bağlamamağımızdır. Fayl ilə işimizi bitirdikdən sonra onu hökmən f.close() metodu ilə bağlamaq lazımdır:

>>> f = open('adlar.txt', 'w')
>>> f.write('Anar\n') 
5
>>> f.write('Rasim\n')
6
>>> f.write('Firudin\n') 
8
>>> f.close() 
>>>

Bir məsələni də qeyd edək ki, f.close() metodu ilə fayl bağlanarkən ona ayrılmış bütün sistem resursları da boşaldılmış olur. Bundan sonra fayl ilə bağlı hər hansı əməliyyatı yerinə yetirmək üçün fayl yenidən açılmalıdır. Əks halda, xəta qeydə alınacaq:

>>> f = open('adlar.txt', 'a') 
>>> f.write('Fuad\n')
5
>>> f.write('Nadir')
5
>>> f.close()
>>> f.write('Tamerlan\n') 
Traceback (most recent call last):
 File "<pyshell#18>", line 1, in <module> 
  f.write('Tamerlan\n')
ValueError: I/O operation on closed file. 
>>>

f.read(ölçü)

Faylın məzmununu oxumaq üçün f.read(ölçü) metodundan istifadə edilir. Bu metodun ölçü olaraq göstərilən parametri fayldan oxunan məlumatın miqdarını göstərmək üçün nəzərdə tutulmuşdur və istifadəsi məcburi deyil:

>>> f = open('adlar.txt', 'r') 
>>> f.read(4)
'Anar'
>>> f.read(10) 
'\nRasim\nFir'
>>> f.close() 
>>>

Gördüyünüz kimi, f.read(ölçü) metodu birinci dəfə çağırılanda faylın əvvəlindən etibarən göstərilən miqdarda məlumatı oxuyur, sonrakı çağırışda isə oxumağa qaldığı yerdən davam edir. Metodun parametri boş buraxıldıqda və ya mənfi göstərildikdə isə faylın bütün məzmunu oxunmuş olur:

>>> f = open('adlar.txt', 'r')
>>> f.read() 
'Anar\nRasim\nFirudin\nFuad\nNadir' 
>>> f.read()
''
>>> f.close() 
>>>

Yuxarıdakı misaldan həm də onu görmək mümkündür ki, faylın bütün məlumatları (hissə-hissə və ya birdən) oxunduqdan sonra yenidən f.read(ölçü) metodunu (parametrli və ya parametrsiz) çağıranda o bizə boş sətir qaytarır.

f.readline()

Bu metod fayldan bir sətir oxuyur və sonundakı \n simvolunu saxlayır. Bildiyiniz kimi, \n simvolu sətir sonunu bildirir və kursoru növbəti sətrə keçirir. Faylın sonunda \n simvolu yoxdursa, yəni fayl boş sətirlə bitmirsə, f.readline() metodu sonuncu sətri oxuyanda onun sonunda \n simvolunu verməyəcək:

>>> f = open('adlar.txt', 'r') 
>>> f.readline()
'Anar\n'
>>> f.readline()
'Rasim\n'
>>> f.readline() 
'Firudin\n'
>>> f.readline() 
'Fuad\n'
>>> f.readline() 
'Nadir'
>>> f.readline() 
''
>>> f.close() 
>>>

Faylın bütün sətirləri oxunduqdan sonra f.readline() metoduna müraciət etdikdə o bizə boş sətri qaytaracaq.
Fayldan məlumatları sətir-sətir oxumağın alternativ yolu isə fayl obyekti üzrə dövrün qurulmasıdır:

>>> f = open('adlar.txt','r') 
>>> for line in f:
        print(line, end = '')

Anar
Rasim 
Firudin
Fuad
Nadir
>>> f.close() 
>>>

Bu üsül sadədir, yaddaşdan səmərəli istifadə edir və daha sürətlidir.

f.readlines()

Bu metod fayldan bütün sətirləri oxuyur və siyahı şəklində qaytarır:

>>> f = open('adlar.txt', 'r')
>>> f.readlines()
['Anar\n', 'Rasim\n', 'Firudin\n', 'Fuad\n', 'Nadir'] 
>>> f.close()
>>>

f.tell()

Bu metod fayl obyektinin faylda cari mövqeyini tam ədəd olaraq qaytarır. İkilik rejimdə (binary mode) bu ədəd faylın əvvəlindən cari mövqeyə qədər olan baytların sayıdır. Mətn rejimində (text mode) isə qaytarılan mövqe anlaşılmaz bir ədəd olur. Aşağıda f.tell() metodunu misallar üzərində nəzərdən keçirəcəyik.
Gəlin əvvəlcə ikilik rejimdə yeni bir fayl açaq və ora ‘alma’ sözünü yazaq:

>>> f = open('test', 'wb')
>>> f.write('alma'.encode('ascii')) 
4
>>> f.close()
>>>

Nəzərinizə çatdırım ki, bu fayl ikilik fayl olduğuna görə ora yazılacaq məlumatlar da ikilik olmalıdır. Məhz buna görə biz ‘alma’ sözünü fayla yazmazdan öncə onu encode() sətir metodundan istifadə edərək ASCII kod sistemində kodlaşdırıb ikilik obyektə çeviririk. İndi isə bu fayldan məlumatları oxuyub fayl obyektinin neçənci mövqedə olduğunu müəyyən edə bilərik:

>>> f = open('test', 'rb')
>>> f.read(1) 
b'a'
>>> f.tell()
1
>>> f.read(2) 
b'lm'
>>> f.tell() 
3
>>> f.read(1) 
b'a'
>>> f.tell() 
4
>>> f.close() 
>>>

f.seek(sürüşmə, haradan)

Bu metoddan fayl obyektinin mövqeyini dəyişmək üçün istifadə olunur. Mövqe istinad nöqtəsinin üzərinə sürüşmə qiymətini gəlməklə hesablanır. İstinad nöqtəsi isə haradan parametrinə görə müəyyən edilir. Daha dəqiq desək, istinad nöqtəsi haradan parametrinin qiyməti 0 olduqda faylın əvvəli, 1 olduqda cari mövqe, 2 olduqda isə faylın sonu kimi qəbul edilir. Biz haradan parametrini boş da buraxa bilərik, bu halda, onun qiyməti default kimi 0 təyin ediləcək və istinad nöqtəsi də faylın əvvəli olacaq. Aşağıdakı misalları nəzərdən keçirin:

>>> f = open('test', 'r+b')
>>> f.write('alma '.encode('ascii')) 
5
>>> f.write('armud '.encode('ascii')) 
6
>>> f.write('ananas'.encode('ascii')) 
6
>>> f.seek(0, 0)
0
>>> f.read(4)
b'alma'
>>> f.seek(1, 1)
5
>>> f.read(5)
b'armud'
>>> f.seek(-6, 2)
11
>>> f.read(6)
b'ananas'
>>> f.close()
>>>

Mətn faylları ilə işləyərkən f.seek(sürüşmə, haradan) metodunda istinad nöqtəsi kimi yalnız faylın əvvəlindən istifadə etməyə icazə verilir. Bu zaman müstəsna hal kimi f.seek(0, 2) ilə faylın sonuna gedilməsinə yol verilir.
Fayla mürəkkəb strukturlu verilənləri yazmaq üçün pickle və json modullarının imkanlarından istifadə etmək olar. Bu modullarla bağlı Python dilinin rəsmi dokumentasiyasına (texniki sənəd) (https://docs.python.org/release/3.13.9/) baxa bilərsiniz.

Yenidən yüklənmə

Effektivlik baxımından interpretatorla hər iş seansında modullar yalnız bir dəfə daxil edilir. Odur ki, əgər modul kodlarında hər hansı dəyişiklik etmisinizsə, bu dəyişikliklərin qüvvəyə minməsi üçün ya interpretator yenidən işə salınmalı, ya da daxil etdiyiniz modul yenidən yüklənməlidir.
Modulun yenidən yüklənməsi importlib modulunun reload() funksiyası vasitəsilə həyata keçirilir. Bu funksiya çağırılmazdan öncə yenidən yükləmək istədiyimiz modul bir daha daxil edilməlidir. Mövzunun tam anlaşılması üçün gəlin bunu təcrübədə tətbiq edək. Nümunə üçün yuxarıda hazırladığımız my_module modulunun quadequ() funksiyasında cüzi bir dəyişiklik edib bu modulu yenidən yükləyəcəyik.
Xatırlayırsınızsa, quadequ() funksiyasında kvadrat tənliyin real kökü olmayanda print(‘No real roots.’) ifadəsi icra edilirdi. Biz bu funksiyanı bir dəfə icra etdikdən sonra modula daxil olub həmin ifadəni print(‘Həqiqi köklər yoxdur.’) ilə əvəzləyəcəyik. Sonra my_module modulunu yenidən yükləyib quadequ() funksiyasının yenilənmiş variantını çağıracağıq:

>>> import my_module
>>> import importlib
>>> my_module.quadequ(1, 2, 3)
No real roots.
>>> import my_module
>>> importlib.reload(my_module)
<module 'my_module' from 'C:\\Python34\\my_module.py'>
>>> my_module.quadequ(1, 2, 3)
Həqiqi köklər yoxdur.
>>>

Burada my_module modulunda müvafiq dəyişiklikləri etdikdən sonra onu yaddaşa verməyi unutmayın.