@property dekoratori bilan tanishish
Python dasturlash bizga ob'ektga yo'naltirilgan dasturlashda getter va setterlardan foydalanishni ancha osonlashtiradigan tayyor @property dekoratorini taqdim etadi.
@property dekoratori nima ekanligi haqida tafsilotlarga o'tishdan oldin, keling avval umuman nima uchun kerak bo'lishi haqida tasavvur hosil qilamiz.
Aytaylik, biz Selsiy darajalarida haroratni saqlovchi klass yaratishga qaror qildik. Va, u haroratni Farengeyt darajalariga aylantiruvchi metodni ham amalga oshiradi.
Buni qilishning bir usuli quyidagicha:
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
Biz bu klassdan obyektlar yaratishimiz va temperature atributini xohlagancha boshqarishimiz mumkin:
# Pythonda atributlarni belgilash va olishning oddiy usuli
class Celsius:
def __init__(self, temperature=0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
# Yangi obyekt yaratish
human = Celsius()
# Haroratni belgilash
human.temperature = 37
# temperature atributini olish
print(human.temperature)
# to_fahrenheit metodini olish
print(human.to_fahrenheit())
Natija
37
98.60000000000001
Bu yerda, Farengeytga aylantirilganda paydo bo'lgan qo'shimcha o'nlik xonalar Floating Point Arithmetic Error (kasr sonlarni hisoblashdagi aniqlik xatosi) sababli.
Shunday qilib, yuqorida ko'rsatilganidek temperature kabi har qanday obyekt atributini berganimizda yoki olganimizda, Python uni obyektning tayyor __dict__ lug'at atributida qidiradi
print(human.__dict__)
# Natija: {'temperature': 37}
Shuning uchun, human.temperature ichki tomondan human.__dict__['temperature']ga aylanadi.
Aytaylik, biz yuqorida aniqlangan Celsius klassining foydalanish imkoniyatini kengaytirmoqchimiz. Bizga ma'lumki, har qanday obyektning harorati -273.15 Selsiy darajasidan pastga tusha olmaydi.
Keling, bu qiymat cheklovini amalga oshirish uchun kodimizni yangilaymiz.
Yuqoridagi cheklovga aniq yechim temperature atributini yashirish (uni maxfiy qilish) va uni boshqarish uchun yangi getter va setter metodlarini aniqlash bo'ladi.
Buni quyidagicha qilish mumkin:
# Getter va Setter metodlarini yaratish
class Celsius:
def __init__(self, temperature=0):
self.set_temperature(temperature)
def to_fahrenheit(self):
return (self.get_temperature() * 1.8) + 32
# getter metodi
def get_temperature(self):
return self._temperature
# setter metodi
def set_temperature(self, value):
if value < -273.15:
raise ValueError("Temperature below -273.15 is not possible.")
self._temperature = value
Ko'rib turganimizdek, yuqoridagi usul ikkita yangi get_temperature() va set_temperature() metodlarini kiritadi.
Bundan tashqari, temperature _temperature bilan almashtirildi. Boshida pastki chiziq _ Pythonda maxfiy (private) o'zgaruvchilarni belgilash uchun ishlatiladi.
Endi, keling bu implementatsiyani ishlatamiz:
# Getter va Setter metodlarini yaratish
class Celsius:
def __init__(self, temperature=0):
self.set_temperature(temperature)
def to_fahrenheit(self):
return (self.get_temperature() * 1.8) + 32
# getter metodi
def get_temperature(self):
return self._temperature
# setter metodi
def set_temperature(self, value):
if value < -273.15:
raise ValueError("Temperature below -273.15 is not possible.")
self._temperature = value
# Yangi obyekt yaratish, set_temperature() __init__() tomonidan ichki chaqiriladi
human = Celsius(37)
# temperature atributini getter orqali olish
print(human.get_temperature())
# to_fahrenheit metodini olish, get_temperature() metodning o'zi tomonidan chaqiriladi
print(human.to_fahrenheit())
# yangi cheklov implementatsiyasi
human.set_temperature(-300)
# to_fahreheit metodini olish
print(human.to_fahrenheit())
Natija
37
98.60000000000001
Traceback (most recent call last):
File "<string>", line 30, in <module>
File "<string>", line 16, in set_temperature
ValueError: Temperature below -273.15 is not possible.
Bu yangilanish yangi cheklovni muvaffaqiyatli amalga oshirdi. Endi bizga haroratni -273.15 Selsiy darajasidan pastga o'rnatishga ruxsat berilmaydi.
Eslatma: Maxfiy o'zgaruvchilar Pythonda aslida mavjud emas. Ular shunchaki rioya qilinishi kerak bo'lgan me'yorlar. Tilning o'zi hech qanday cheklov qo'llamaydi.
Biroq, yuqoridagi yangilanishdagi kattaroq muammo shundaki, bizning avvalgi klassimizni amalga oshirgan barcha dasturlar o'z kodlarini obj.temperaturedan obj.get_temperature()ga va obj.temperature = val kabi barcha ifodalarni obj.set_temperature(val)ga o'zgartirishlari kerak.
Bu qayta ishlab chiqish (refactoring) yuz minglab qatorlardagi kodlar bilan ishlashda muammo tug'dirishi mumkin.
Xullas, bizning yangi yangilanishimiz orqaga qarab mos (backwards compatible) emas edi. Aynan shu yerda @property yordamga keladi.
Yuqoridagi muammoni hal qilishning pythonik usuli — property klassidan foydalanish. Mana kodimizni qanday yangilashimiz mumkin:
# property klassidan foydalanish
class Celsius:
def __init__(self, temperature=0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
# getter
def get_temperature(self):
print("Getting value...")
return self._temperature
# setter
def set_temperature(self, value):
print("Setting value...")
if value < -273.15:
raise ValueError("Temperature below -273.15 is not possible")
self._temperature = value
# property obyektini yaratish
temperature = property(get_temperature, set_temperature)
Biz get_temperature() va set_temperature() ichiga ular bajarilayotganini aniq kuzatish uchun print() funksiyasini qo'shdik.
Kodning oxirgi qatori temperature property obyektini yaratadi. Oddiy qilib aytganda, property ba'zi kodni (get_temperature va set_temperature) a'zo atributga murojaatlarga (temperature) biriktiradi.
Keling, bu yangilangan kodni ishlatamiz:
# property klassidan foydalanish
class Celsius:
def __init__(self, temperature=0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
# getter
def get_temperature(self):
print("Getting value...")
return self._temperature
# setter
def set_temperature(self, value):
print("Setting value...")
if value < -273.15:
raise ValueError("Temperature below -273.15 is not possible")
self._temperature = value
# property obyektini yaratish
temperature = property(get_temperature, set_temperature)
human = Celsius(37)
print(human.temperature)
print(human.to_fahrenheit())
human.temperature = -300
Natija
Setting value...
Getting value...
37
Getting value...
98.60000000000001
Setting value...
Traceback (most recent call last):
File "<string>", line 31, in <module>
File "<string>", line 18, in set_temperature
ValueError: Temperature below -273 is not possible
Ko'rib turganimizdek, temperaturening qiymatini oladigan har qanday kod avtomatik ravishda lug'at (__dict__) qidiruvi o'rniga get_temperature()ni chaqiradi.
Xuddi shunday, temperaturega qiymat beradigan har qanday kod avtomatik ravishda set_temperature()ni chaqiradi.
Biz hatto obyekt yaratganimizda ham set_temperature() chaqirilganini yuqorida ko'rishimiz mumkin.
human = Celsius(37) # Setting value... ni chop etadi
Nima uchun ekanligini taxmin qila olasizmi?
Sababi shundaki, obyekt yaratilganda, __init__() metodi chaqiriladi. Bu metodda self.temperature = temperature qatori bor. Bu ifoda avtomatik ravishda set_temperature()ni chaqiradi.
Xuddi shunday, c.temperature kabi har qanday murojaat avtomatik ravishda get_temperature()ni chaqiradi. Aynan shuni property amalga oshiradi.
propertydan foydalanib, biz qiymat cheklovi implementatsiyasida hech qanday o'zgartirish talab qilinmasligini ko'rishimiz mumkin. Shunday qilib, bizning implementatsiyamiz orqaga qarab mos (backward compatible).
Eslatma: Haqiqiy harorat qiymati maxfiy _temperature o'zgaruvchisida saqlanadi. temperature atributi esa bu maxfiy o'zgaruvchiga interfeys taqdim etuvchi property obyekti.
Pythonda, property() — property obyektini yaratuvchi va qaytaruvchi tayyor funksiya. Bu funksiyaning sintaksisi:
property(fget=None, fset=None, fdel=None, doc=None)
Bu yerda,
fget — atributning qiymatini olish uchun funksiyafset — atributning qiymatini belgilash uchun funksiyafdel — atributni o'chirish uchun funksiyadoc — satr (izohga o'xshash)Implementatsiyadan ko'rib turganimizdek, bu funksiya argumentlari ixtiyoriy.
Property obyekti fget, fset va fdelni keyinroq belgilash uchun uchta metodga ega: getter(), setter() va deleter(). Bu shuni anglatadiki, quyidagi qator:
temperature = property(get_temperature,set_temperature)
quyidagicha bo'linishi mumkin:
# bo'sh property yasash
temperature = property()
# fget'ni berish
temperature = temperature.getter(get_temperature)
# fset'ni berish
temperature = temperature.setter(set_temperature)
Bu ikki kod bo'lagi ekvivalent.
Python Decorators bilan tanish dasturchilar yuqoridagi konstruksiyani dekorator sifatida amalga oshirish mumkinligini payqashi mumkin.
Biz hatto get_temperature va set_temperature nomlarini ham aniqlamasligimiz mumkin, chunki ular keraksiz va klass nom maydonini ifloslantiradi.
Buning uchun, biz getter va setter funksiyalarimizni aniqlashda temperature nomidan qayta foydalanamiz. Keling, buni dekorator sifatida qanday amalga oshirishga qaraymiz:
class Celsius:
def __init__(self, temperature=0):
# obyekt yaratilganda, setter metodi avtomatik chaqiriladi
self.temperature = temperature
def to_fahrenheit(self):
# haroratni Farengeytga aylantirish
return (self.temperature * 1.8) + 32
@property
def temperature(self):
print("Getting value...")
return self._temperature
@temperature.setter
def temperature(self, value):
print("Setting value...")
# harorat absolyut noldan pastga tushmasligini ta'minlash
if value < -273.15:
raise ValueError("Temperature below -273.15°C is not possible")
self._temperature = value
# yaroqli harorat bilan obyekt yaratish
human = Celsius(37)
# haroratni Selsiyda chop etish
print(human.temperature)
# haroratni Farengeytda chop etish
print(human.to_fahrenheit())
# -273.15°C dan past harorat bilan obyekt yaratishga urinish xatolik tashlaydi
try:
coldest_thing = Celsius(-300)
except ValueError as e:
print(e)
Natija
Setting value...
Getting value...
37
Getting value...
98.60000000000001
Setting value...
ValueError: Temperature below -273 is not possible
Yuqoridagi implementatsiya sodda va samarali. Bu propertydan foydalanishning tavsiya etilgan usuli.