Python @property: Jak to používat a proč? - Programiz

V tomto výukovém programu se dozvíte o dekorátoru Python @property; pythonický způsob použití getrů a setterů v objektově orientovaném programování.

Programování v Pythonu nám poskytuje vestavěný @propertydekorátor, díky němuž je používání getru a setteru mnohem jednodušší v objektově orientovaném programování.

Než se @propertypustíme do podrobností o tom, co je to dekoratér, nejprve si vytvořme intuici, proč by to vůbec bylo potřeba.

Třída bez getterů a setterů

Předpokládejme, že se rozhodneme vytvořit třídu, která uchovává teplotu ve stupních Celsia. Rovněž by implementovalo metodu převodu teploty na stupně Fahrenheita. Jeden způsob, jak toho dosáhnout, je následující:

 class Celsius: def __init__(self, temperature = 0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32

Z této třídy můžeme vytvářet objekty a manipulovat s temperatureatributem, jak si přejeme:

 # 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())

Výstup

 37 98.60000000000001

Extra desetinná místa při převodu na Fahrenheita jsou způsobena aritmetickou chybou s plovoucí desetinnou čárkou. Další informace najdete v aritmetické chybě s plovoucí desetinnou čárkou v Pythonu.

Kdykoli přiřadíme nebo načteme jakýkoli atribut objektu, temperaturejak je uvedeno výše, Python jej prohledá v __dict__atributu vestavěného slovníku objektu.

 >>> human.__dict__ ('temperature': 37)

Proto se man.temperaturevnitřně stává man.__dict__('temperature').

Používání getrů a setrů

Předpokládejme, že chceme rozšířit použitelnost třídy Celsia definované výše. Víme, že teplota žádného objektu nemůže dosáhnout pod -273,15 stupňů Celsia (absolutní nula v termodynamice)

Aktualizujme náš kód, abychom implementovali toto omezení hodnoty.

Zřejmým řešením výše uvedeného omezení bude skrýt atribut temperature(nastavit jej jako soukromý) a definovat nové metody getter a setter pro manipulaci s ním. To lze provést následovně:

 # 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

Jak vidíme, výše uvedená metoda zavádí dvě nové get_temperature()a set_temperature()metody.

Dále temperaturebyl nahrazen _temperature. Podtržítko _na začátku se používá k označení soukromých proměnných v Pythonu.

Nyní použijeme tuto implementaci:

 # 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())

Výstup

 37 98.60000000000001 Traceback (poslední poslední hovor): Soubor "", řádek 30, v souboru "", řádek 16, v set_temperature ValueError: Teplota pod -273,15 není možná.

Tato aktualizace úspěšně implementovala nové omezení. Již nemůžeme nastavovat teplotu pod -273,15 stupňů Celsia.

Poznámka : Soukromé proměnné ve skutečnosti v Pythonu neexistují. Je prostě třeba dodržovat normy. Samotný jazyk neplatí žádná omezení.

 >>> human._temperature = -300 >>> human.get_temperature() -300

Nicméně, větší problém s výše aktualizace je, že všechny programy, které zavedly náš předchozí třídu muset změnit svůj kód z obj.temperaturek obj.get_temperature()a všechny výrazy jako obj.temperature = valpro obj.set_temperature(val).

Toto refaktorování může způsobit problémy při řešení stovek tisíc řádků kódů.

Celkově vzato nebyla naše nová aktualizace zpětně kompatibilní. Tady @propertypřichází záchrana.

Vlastnost Class

Pythonickým způsobem řešení výše uvedeného problému je použití propertytřídy. Takto můžeme aktualizovat náš kód:

 # 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)

Přidali jsme print()funkci dovnitř get_temperature()a set_temperature()abychom jasně viděli, že se provádějí.

Poslední řádek kódu vytvoří objekt vlastnosti temperature. Jednoduše řečeno, vlastnost připojí nějaký kód ( get_temperaturea set_temperature) k atributu člena accesses ( temperature).

Pojďme použít tento aktualizační kód:

 # 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

Výstup

 Nastavení hodnoty … Získání hodnoty … 37 Získání hodnoty … 98.60000000000001 Nastavení hodnoty … Traceback (poslední hovor poslední): Soubor "", řádek 31, v souboru "", řádek 18, v set_temperature ValueError: Teplota pod -273 není možná

Jak vidíme, jakýkoli kód, který načte hodnotu, temperaturebude automaticky volat get_temperature()místo vyhledávání slovníku (__dict__). Podobně temperaturebude automaticky volat jakýkoli kód, který přiřadí hodnotu set_temperature().

Můžeme dokonce vidět výše, že to set_temperature()bylo voláno, i když jsme vytvořili objekt.

 >>> human = Celsius(37) Setting value… 

Uhodnete proč?

Důvodem je to, že když je objekt vytvořen, __init__()metoda se zavolá. Tato metoda má řádek self.temperature = temperature. Tento výraz automaticky volá set_temperature().

Podobně jakýkoli přístup, jako je c.temperatureautomatické volání get_temperature(). To je to, co dělá vlastnost. Zde je několik dalších příkladů.

 >>> human.temperature Getting value 37 >>> human.temperature = 37 Setting value >>> c.to_fahrenheit() Getting value 98.60000000000001

Použitím propertyvidíme, že při implementaci omezení hodnoty není nutná žádná změna. Naše implementace je tedy zpětně kompatibilní.

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)

Tyto dva kódy jsou ekvivalentní.

Programátoři obeznámení s dekorátory Pythonu mohou rozpoznat, že výše uvedený konstrukt lze implementovat jako dekoratéry.

Dokonce nemůžeme definovat názvy get_temperaturea set_temperatureprotože jsou zbytečné a znečišťují jmenný prostor třídy.

Z tohoto důvodu znovu používáme temperaturenázev a zároveň definujeme naše funkce getter a setter. Podívejme se, jak to implementovat jako dekoratér:

 # 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)

Výstup

 Nastavení hodnoty … Získání hodnoty … 37 Získání hodnoty … 98.60000000000001 Nastavení hodnoty … Traceback (poslední volání poslední): Soubor "", řádek 29, v souboru "", řádek 4, v __init__ Soubor "", řádek 18, v teplotě ValueError: Teplota pod -273 ° C není možná

Výše uvedená implementace je jednoduchá a efektivní. Je to doporučený způsob použití property.

Zajímavé články...