Питхон @проперти: Како га користити и зашто? - Програмиз

У овом упутству ћете научити о Питхон @проперти децоратор; питонски начин употребе гетера и сетера у објектно оријентисаном програмирању.

Програмирање на Питхону пружа нам уграђени @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 attribute
  • fset is function to set value of the attribute
  • fdel is function to delete the attribute
  • doc 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.

Занимљиви Чланци...