# Python-da classlar (Hissə 2): Dəyişənlər

### Giriş

Əvvəlki məqalədə class və obyektlərin yaradılması haqqında bəhs etmişdim. İndi isə classda dəyişənlər haqqında danışmaq lazımdır. Dəyişənlər (ingcə. *variables*) obyekt daxilində məlumatları yadda saxlamaq üçün istifadə olunur. Classda iki növ dəyişən tipi mövcuddur:

1. İnstansiya dəyişənləri (ingcə. *instance variables*)
2. Class dəyişənləri (ingcə. *class variables*)

```python
class Person:
    nationality = "Azerbaijani"		# <- class variable
    
	def __init__(self, f_name, l_name, age):
        self.f_name = f_name		# <--| 
        self.l_name = l_name		# <--| instance variables
        self.age = age				# <--|

```

### İnstansiya dəyişənləri

Yuxarıdakı kod parçası sənə əvvəlki məqalədən tanış gələ bilər. İnstansiya dəyişənlərinin ən məşhur əlavə edilmə yeri elə məhz `__init__` metodudur və təkcə bu metodla limitlənmir. İstənilən metodda `self`-dən istifadə edilərək yaradılan dəyişənlər instansiya dəyişənləri sayılır.

```python
person_1 = Person("Javid", "Khasizada", 21)
person_2 = Person("Tarigulu", "Khasizada", 26)

print(person_1.f_name, person_2.l_name)	# output: Javid, Tarigulu
```

`name` obyekt dəyişəni olduğuna görə yaratdığımız hər iki obyekt üçün müxtəlif nəticə verir.  İnstansiya dəyişənlərini istifadə edə bilmək üçün `object.name_of_variable` şəklində yazmaq lazımdır.

### Class dəyişənləri

`Person` classında gördüyümüz `nationality` dəyişəni bir class dəyişənidir. Bu tip dəyişənlər əsasən daha ümumi və ya hamı üçün eyni olan məlumatları yadda saxlamaq üçün istifadə edilir. Class dəyişənlərini istifadə edə bilmək üçün `Class.name_of_variable` şəklində yazmaq lazımdır.

```python
print(Person.nationality)			# output: Azerbaijani
```

Məsələn, bu nümunədə fərz edirik ki, bütün yaradılacaq insan obyektləri Azərbaycan vətəndaşıdır.

Əlavə olaraq bu dəyişənlərdən həmin classa aid olan bütün obyektlər istifadə edə bilir və işlədilmə şəkli eyni instansiya dəyişənlərində olduğu kimidir.

```python
print(person_1.nationality)			# output: Azerbaijani
```

Metodlar daxilində class dəyişənlərinin istifadəsi `self`-dən yararlanmaqla mümkündür. Bunu aşağıdakı koddan görə bilərik.

```python
class Person:
    nationality = "Azerbaijani"		
    
	# some code
    
    def tell_nationality(self):
        return f'{self.f_name} is {self.nationality}.'

print(person_1.tell_nationality)	# Javid is Azerbaijani.
```


### Dəyişənlər necə dəyişir?

İnstansiya dəyişənlərinin dəyərlərini dəyişmək sadəcə onlara yeni dəyərlər verməkdən ibarətdir. Bunu kodun 3-cü sətirindən də görmək olar. 

```python
person_1 = Person("Javid", "Khasizada", 21)

person_1.f_name = "JAXA"
```

Lakin class dəyişənlərində məsələ nisbətən fərqlidir. Gəlin aşağıdakı koda nəzər salaq.

```python
Person.nationality = "Turkish"
print(person_1.nationality) 	# output: ???
print(person_2.nationality) 	# output: ???

person_1.nationality = "Hungarian"
print(person_1.nationality) 	# output: ???	
print(person_2.nationality) 	# output: ???	
```

Kodda biz birinci classın özündən istifadə edərək class dəyişənini yeniləyirik, ikinci olaraq isə yaratdığımız obyekt ilə class dəyişəninə yeni dəyər veririk. Səncə ekrana hansı dəyərlər çap ediləcək?

```python
Person.nationality = "Turkish"
print(person_1.nationality) 	# output: Turkish
print(person_2.nationality) 	# output: Turkish

person_1.nationality = "Hungarian"
print(person_1.nationality) 	# output: Hungarian	
print(person_2.nationality) 	# output: Turkish
```

Ümid edirəm düz tapa bildin. İndi isə gəl cavaba baxaq. Class səviyyəsində dəyişiklik edilən zaman bu bütün obyektlərdə həmin dəyərin yenilənməsi ilə nəticələnir.

Amma class dəyişənlərini obyekt səviyyəsində dəyişəndə o yalnız həmin obyektə dəyişmiş olur və qalan obyektlərə təsirsiz qalır. Bu səbəbdən də `person_2` obyektinin milliyəti hələ də eynidir (sətir 7). 

> `person_2` dəyişəninin milliyətinin `Turkish` olmasının səbəbi 1-ci sətirdə etdiyimiz dəyişiklikdir.

Bunun səbəbi dəyişənləri qaytararkən Pythonun **Method Resolution Order (MRO)** prinsipindən istifadə etməsidir.  *Bu mövzuya bir daha geri dönəcək olduğuma görə burda qısaca izah edəcəm.* MRO Pythonun dəyişən və ya metodların dəyərlərinin nə olduğunu təyin etməyə kömək edir və kiçikdən böyüyə (obyektdən class-a doğru) doğru dəyəri axtarır. İlk tapdığı dəyəri isə geri qaytarır. Biz `person_1` obyektində `nationality` dəyişənini obyekt səviyyəsində yenidən təyin edildiyinə (sətir 5) görə artıq Python class səviyyəsində bu dəyərin nə olduğuna baxmır və `Hungarian` dəyərini çap edir. 

> Bu mövzunu bir daha növbəti məqalələrdə ətraflı şəkildə izah etməyə çalışacam.

Biz bu nümunədən onu öyrənə bilərik ki, əgər bütün obyektlərin dəyərini dəyişmək istəyiriksə, class səviyyəsində, yalnız bir obyektə təsir etmək istəyiriksə, obyekt səviyyəsində class dəyişəninə yeni dəyər verməliyik.

**Sual:** Bəs birinci obyektdə dəyişiklik edib sonra class səviyyəsində dəyişiklik  etsək nə baş verər?

```python
person_1.nationality = "Hungarian"
print(person_1.nationality) 	# output: Hungarian	

Person.nationality = "Turkish"
print(person_1.nationality) 	# output: ???
```

 Nə düşünürsən, `Hungarian` yoxsa `Turkish`? Məhz MRO prinsipinə görə cavab `Hungarian` olacaq. 

### Məzmun

Beləliklə bu məqalənin sonuna çatdıq. Bu məqalədə aşağıdakıları öyrəndik:

1. İnstansiya dəyişənləri
2. Class dəyişənləri
3. Dəyişənlərin yenidən təyin edilməsi
4. MRO prinsipinin səthi izahı

### İstinad mənbələri
* https://medium.com/python-features/class-vs-instance-variables-8d452e9abcbd
* https://www.youtube.com/watch?v=BJ-VvGyQxho&
* https://www.geeksforgeeks.org/method-resolution-order-in-python-inheritance/

