У овом упутству ћете научити о Питхон @проперти децоратор; питонски начин употребе гетера и сетера у објектно оријентисаном програмирању.
Програмирање на Питхону пружа нам уграђени @property
декоратор који много олакшава употребу гетера и постављача у објектно оријентисаном програмирању.
Пре него што улазимо у детаље о томе шта је @property
декоратор, прво направимо интуицију о томе зашто би он уопште био потребан.
Разред без гетера и сетера
Претпоставимо да смо одлучили да направимо класу која чува температуру у степени Целзијуса. Такође би применио методу за претварање температуре у степене Фахренхеита. Један од начина за то је следећи:
class Celsius: def __init__(self, temperature = 0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32
Можемо направити предмете од ове класе и манипулисати temperature
атрибутом како желимо:
# Basic method of setting and getting attributes in Python class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 # Create a new object human = Celsius() # Set the temperature human.temperature = 37 # Get the temperature attribute print(human.temperature) # Get the to_fahrenheit method print(human.to_fahrenheit())
Оутпут
37 98.60000000000001
Додатне децимале при конвертовању у Фахренхеит су због аритметичке грешке са покретним зарезом. Да бисте сазнали више, посетите Питхон аритметичку грешку са покретном тачком.
Кад год додијелимо или дохватимо било који атрибут објекта као temperature
што је приказано горе, Питхон га претражује у уграђеном __dict__
атрибуту рјечника објекта .
>>> human.__dict__ ('temperature': 37)
Према томе, man.temperature
интерно постаје man.__dict__('temperature')
.
Коришћење гетера и сетера
Претпоставимо да желимо да проширимо употребљивост класе Целзијуса дефинисане горе. Знамо да температура било ког објекта не може да досегне испод -273,15 степени Целзијуса (апсолутна нула у термодинамици)
Ажурирајмо наш код да бисмо применили ово ограничење вредности.
Очигледно решење за горње ограничење биће сакривање атрибута temperature
(учинити га приватним) и дефинисање нових метода гетера и постављача за манипулисање њиме. То се може учинити на следећи начин:
# Making Getters and Setter methods class Celsius: def __init__(self, temperature=0): self.set_temperature(temperature) def to_fahrenheit(self): return (self.get_temperature() * 1.8) + 32 # getter method def get_temperature(self): return self._temperature # setter method def set_temperature(self, value): if value < -273.15: raise ValueError("Temperature below -273.15 is not possible.") self._temperature = value
Као што видимо, горња метода уводи две нове get_temperature()
и set_temperature()
методе.
Даље, temperature
замењен је са _temperature
. Подвлака _
на почетку користи се за означавање приватних променљивих у Питхону.
Сада, искористимо ову имплементацију:
# Making Getters and Setter methods class Celsius: def __init__(self, temperature=0): self.set_temperature(temperature) def to_fahrenheit(self): return (self.get_temperature() * 1.8) + 32 # getter method def get_temperature(self): return self._temperature # setter method def set_temperature(self, value): if value < -273.15: raise ValueError("Temperature below -273.15 is not possible.") self._temperature = value # Create a new object, set_temperature() internally called by __init__ human = Celsius(37) # Get the temperature attribute via a getter print(human.get_temperature()) # Get the to_fahrenheit method, get_temperature() called by the method itself print(human.to_fahrenheit()) # new constraint implementation human.set_temperature(-300) # Get the to_fahreheit method print(human.to_fahrenheit())
Оутпут
37 98.60000000000001 Трацебацк (последњи последњи позив): Датотека "", ред 30, у датотеци "", ред 16, у сет_температуре ВалуеЕррор: Температура испод -273,15 није могућа.
Ово ажурирање је успешно применило ново ограничење. Више не смемо да подесимо температуру испод -273,15 степени Целзијуса.
Напомена : Приватне променљиве заправо не постоје у Питхону. Једноставно постоје норме којих се треба придржавати. Сам језик не примењује никаква ограничења.
>>> human._temperature = -300 >>> human.get_temperature() -300
Међутим, већи проблем са горе упдате је да су сви програми који спроводе нашу претходну класу морати да модификује свој код, obj.temperature
да се obj.get_temperature()
и све изразе као obj.temperature = val
да obj.set_temperature(val)
.
Ова рефакторизација може створити проблеме док се бавите стотинама хиљада редова кодова.
Све у свему, наше ново ажурирање није било уназад компатибилно. Овде @property
долази спашавање.
Класа имовине
Питонски начин за решавање горе наведеног проблема је коришћење property
класе. Ево како можемо ажурирати наш код:
# using property class 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 # creating a property object temperature = property(get_temperature, set_temperature)
Додали смо print()
функцију унутра get_temperature()
и set_temperature()
да јасно посматрамо да се извршавају.
Последњи ред кода чини својство објекта temperature
. Једноставно речено, својство придружује неки код ( get_temperature
и set_temperature
) приступу атрибута члана ( temperature
).
Користимо овај код за ажурирање:
# using property class 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 # creating a property object temperature = property(get_temperature, set_temperature) human = Celsius(37) print(human.temperature) print(human.to_fahrenheit()) human.temperature = -300
Оутпут
Постављање вредности … Добијање вредности … 37 Добијање вредности … 98.60000000000001 Постављање вредности … Трацебацк (последњи последњи позив): Датотека "", ред 31, у датотеци "", ред 18, у поставци_температуре ВалуеЕррор: Температура испод -273 није могућа
Као што видимо, било који код који преузме вредност temperature
аутоматски ће позвати get_temperature()
уместо тражења речника (__дицт__). Слично томе, сваки код који додели вредност temperature
аутоматски ће позвати set_temperature()
.
И горе можемо видети како set_temperature()
се то звало чак и када смо креирали објекат.
>>> human = Celsius(37) Setting value…
Можете ли погодити зашто?
Разлог је тај што се приликом креирања објекта __init__()
метода позива. Ова метода има линију self.temperature = temperature
. Овај израз аутоматски позива set_temperature()
.
Слично томе, сваки приступ попут c.temperature
аутоматског позива get_temperature()
. То је оно што имовина ради. Ево још неколико примера.
>>> human.temperature Getting value 37 >>> human.temperature = 37 Setting value >>> c.to_fahrenheit() Getting value 98.60000000000001
Коришћењем property
видимо да није потребна модификација у примени ограничења вредности. Стога је наша примена компатибилна са уназад.
Note: The actual temperature value is stored in the private _temperature
variable. The temperature
attribute is a property object which provides an interface to this private variable.
The @property Decorator
In Python, property()
is a built-in function that creates and returns a property
object. The syntax of this function is:
property(fget=None, fset=None, fdel=None, doc=None)
where,
fget
is function to get value of the attributefset
is function to set value of the attributefdel
is function to delete the attributedoc
is a string (like a comment)
As seen from the implementation, these function arguments are optional. So, a property object can simply be created as follows.
>>> property()
A property object has three methods, getter()
, setter()
, and deleter()
to specify fget
, fset
and fdel
at a later point. This means, the line:
temperature = property(get_temperature,set_temperature)
can be broken down as:
# make empty property temperature = property() # assign fget temperature = temperature.getter(get_temperature) # assign fset temperature = temperature.setter(set_temperature)
Ова два дела кодова су еквивалентна.
Програмери упознати са Питхон Децораторс могу препознати да се горња конструкција може применити као декоратери.
Не можемо чак ни да дефинишемо имена, get_temperature
а set_temperature
како су непотребна и загађују простор имена класе.
За ово, поново користимо temperature
име док дефинишемо наше геттер и сеттер функције. Погледајмо како ово применити као декоратер:
# Using @property decorator class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): 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… ") if value < -273.15: raise ValueError("Temperature below -273 is not possible") self._temperature = value # create an object human = Celsius(37) print(human.temperature) print(human.to_fahrenheit()) coldest_thing = Celsius(-300)
Оутпут
Постављање вредности … Добијање вредности … 37 Добијање вредности … 98.60000000000001 Постављање вредности … Трацебацк (последњи последњи позив): Датотека "", ред 29, у датотеци "", ред 4, у __инит__ Датотека "", ред 18, у температури ВалуеЕррор: Температура испод -273 није могућа
Горња примена је једноставна и ефикасна. То је препоручени начин употребе property
.