@Property Decorator v Pythone: jeho prípady použitia, výhody a syntax

? Zoznámte sa s vlastnosťami

Vitajte! V tomto článku sa dozviete, ako pracovať s @propertydekoratérom v Pythone.

Naučíš sa:

  • Výhody práce s vlastnosťami v Pythone.
  • Základy funkcií dekoratéra: čo to je a ako súvisia s @property.
  • Ako môžete pomocou @property definovať getry, settery a deletery.

1️⃣ Výhody vlastností v Pythone

Začnime trochou kontextu. Prečo by ste používali vlastnosti v Pythone?

Vlastnosti možno považovať za „pytónsky“ spôsob práce s atribútmi, pretože:

  • Syntax použitá na definovanie vlastností je veľmi stručná a čitateľná.
  • K atribútom inštancií môžete pristupovať presne tak, akoby to boli verejné atribúty, pričom pomocou „kúzla“ sprostredkovateľov (getrov a setterov) overujete nové hodnoty a vyhýbate sa priamemu prístupu alebo zmene údajov.
  • Použitím @property môžete „znovu použiť“ názov vlastnosti, aby ste sa vyhli vytváraniu nových mien pre getry, settery a deletery.

Vďaka týmto výhodám sú vlastnosti skutočne úžasným nástrojom, ktorý vám pomáha písať stručnejší a čitateľnejší kód. ?

2️⃣ Úvod do dekoratérov

Funkcia dekorátora je v podstate funkcia, ktorá pridáva novú funkčnosť k funkcii, ktorá sa odovzdáva ako argument. Používanie funkcie dekorátora je ako pridanie čokoládovej posýpky do zmrzliny?. Umožňuje nám pridať novú funkčnosť k existujúcej funkcii bez jej úpravy.

V príklade nižšie môžete vidieť, ako vyzerá typická funkcia dekoratéra v Pythone:

def decorator(f): def new_function(): print("Extra Functionality") f() return new_function @decorator def initial_function(): print("Initial Functionality") initial_function()

Poďme podrobne analyzovať tieto prvky:

  • Najprv nájdeme funkciu dekoratéra def decorator(f)(postrekovače ✨), ktorá berie funkciu fako argument.
def decorator(f): def new_function(): print("Extra Functionality") f() return new_function
  • Táto funkcia natierač má vnorené funkcie new_function. Všimnite si, ako fsa volá dovnútra, new_functionaby sa dosiahla rovnaká funkčnosť, a to pridaním novej funkcionality pred volaním funkcie (novú funkcionalitu by sme mohli pridať aj po volaní funkcie).
  • Samotná funkcia dekorátora vráti vnorenú funkciu new_function.
  • Potom (dole) nájdeme funkciu, ktorá bude zdobená (zmrzlina?) initial_function. Všimnite si veľmi zvláštnu syntax ( @decorator) nad hlavičkou funkcie.
@decorator def initial_function(): print("Initial Functionality") initial_function()

Ak spustíme kód, uvidíme tento výstup:

Extra Functionality Initial Functionality

Všimnite si, ako funguje funkcia dekoratéra, aj keď iba voláme initial_function(). To je kúzlo pridania @decorator?.

?Poznámka: Všeobecne by sme písali tak @, že nahradíme názov funkcie dekorátora za symbolom @.

Viem, že sa možno pýtate: ako to súvisí s majetkom @? @ Vlastnosť je vstavaný dekorátor funkcie property () v Pythone. Používa sa na to, aby sa niektorým metódam poskytla „špeciálna“ funkčnosť, aby pri definovaní vlastností v triede fungovali ako getre, settery alebo deletery.

Teraz, keď ste oboznámení s dekorátormi, pozrime sa na skutočný scenár použitia @property!

? Scenár v reálnom svete: @vlastnosť

Povedzme, že táto trieda je súčasťou vášho programu. Modelujete dom s Housetriedou (v súčasnosti má trieda definovaný iba atribút cenovej inštancie):

class House: def __init__(self, price): self.price = price

Tento atribút inštancie je verejný, pretože jeho názov nemá úvodné podčiarknutie. Pretože je tento atribút momentálne verejný, je veľmi pravdepodobné, že ste vy a ďalší vývojári vo vašom tíme pristupovali k atribútu priamo v iných častiach programu pomocou bodkovej notácie, napríklad takto:

# Access value obj.price # Modify value obj.price = 40000

? Tip: obj predstavuje premennú, ktorá odkazuje na inštanciu House.

Zatiaľ všetko funguje výborne, však? Ale povedzme, že ste požiadaní, aby sa tento atribút chránený (neverejné) a pred priradením overiť novú hodnotu . Konkrétne musíte skontrolovať, či je hodnota kladná float. Ako by si to urobil Pozrime sa.

Zmena kódu

V tomto okamihu, ak sa rozhodnete pridať getrov a setterov, pravdepodobne vy a váš tím prepadnete panike?. Je to tak preto, lebo každý riadok kódu, ktorý pristupuje k hodnote atribútu alebo ju upravuje, bude potrebné upraviť, aby sa dal nazvať getter alebo setter. V opačnom prípade sa kód pokazí ⚠️.

# Changed from obj.price obj.get_price() # Changed from obj.price = 40000 obj.set_price(40000)

Ale ... Vlastnosti prichádzajú na pomoc! Vďaka tomu @propertyvy a váš tím nebudete musieť upravovať žiadny z týchto riadkov, pretože budete môcť pridávať getry a settery „v zákulisí“ bez ovplyvnenia syntaxe, ktorú ste použili na prístup k atribútu alebo jeho úpravy, keď bol verejný.

Úžasné, že?  

? @property: Syntax a logika

Ak sa rozhodnete pre použitie @property, vaša trieda bude vyzerať ako v príklade nižšie:

class House: def __init__(self, price): self._price = price @property def price(self): return self._price @price.setter def price(self, new_price): if new_price > 0 and isinstance(new_price, float): self._price = new_price else: print("Please enter a valid price") @price.deleter def price(self): del self._price

Pre nehnuteľnosť môžete definovať konkrétne tri spôsoby :

  • Karierista - prístup k hodnotu atribútu.
  • Nastavovač - nastavte hodnotu atribútu.
  • Deleter - odstrániť atribút inštancie.

Cena je teraz „chránená“

Please note that the price attribute is now considered "protected" because we added a leading underscore to its name in self._price:

self._price = price

In Python, by convention, when you add a leading underscore to a name, you are telling other developers that it should not be accessed or modified directly outside of the class. It should only be accessed through intermediaries (getters and setters) if they are available.

? Getter

Here we have the getter method:

@property def price(self): return self._price

Notice the syntax:

  • @property - Used to indicate that we are going to define a property. Notice how this immediately improves readability because we can clearly see the purpose of this method.
  • def price(self) - The header. Notice how the getter is named exactly like the property that we are defining: price. This is the name that we will use to access and modify the attribute outside of the class. The method only takes one formal parameter, self, which is a reference to the instance.
  • return self._price - This line is exactly what you would expect in a regular getter. The value of the protected attribute is returned.

Here is an example of the use of the getter method:

>>> house = House(50000.0) # Create instance >>> house.price # Access value 50000.0

Notice how we access the price attribute as if it were a public attribute. We are not changing the syntax at all, but we are actually using the getter as an intermediary to avoid accessing the data directly.

? Setter

Now we have the setter method:

@price.setter def price(self, new_price): if new_price > 0 and isinstance(new_price, float): self._price = new_price else: print("Please enter a valid price")

Notice the syntax:

  • @price.setter - Used to indicate that this is the setter method for the price property. Notice that we are not using @property.setter, we are using @price.setter. The name of the property is included before .setter.
  • def price(self, new_price): - The header and the list of parameters. Notice how the name of the property is used as the name of the setter. We also have a second formal parameter (new_price), which is the new value that will be assigned to the price attribute (if it is valid).
  • Finally, we have the body of the setter where we validate the argument to check if it is a positive float and then, if the argument is valid, we update the value of the attribute. If the value is not valid, a descriptive message is printed. You can choose how to handle invalid values according the needs of your program.

This is an example of the use of the setter method with @property:

>>> house = House(50000.0) # Create instance >>> house.price = 45000.0 # Update value >>> house.price # Access value 45000.0

Notice how we are not changing the syntax, but now we are using an intermediary (the setter) to validate the argument before assigning it. The new value (45000.0) is passed as an argument to the setter :

house.price = 45000.0

If we try to assign an invalid value, we see the descriptive message. We can also check that the value was not updated:

>>> house = House(50000.0) >>> house.price = -50 Please enter a valid price >>> house.price 50000.0

? Tip: This proves that the setter method is working as an intermediary. It is being called "behind the scenes" when we try to update the value, so the descriptive message is displayed when the value is not valid.

? Deleter

Finally, we have the deleter method:

@price.deleter def price(self): del self._price

Notice the syntax:

  • @price.deleter - Used to indicate that this is the deleter method for the price property. Notice that this line is very similar to @price.setter, but now we are defining the deleter method, so we write @price.deleter.
  • def price(self): - The header. This method only has one formal parameter defined, self.
  • del self._price - The body, where we delete the instance attribute.

? Tip: Notice that the name of the property is "reused" for all three methods.

This is an example of the use of the deleter method with @property:

# Create instance >>> house = House(50000.0) # The instance attribute exists >>> house.price 50000.0 # Delete the instance attribute >>> del house.price # The instance attribute doesn't exist >>> house.price Traceback (most recent call last): File "", line 1, in  house.price File "", line 8, in price return self._price AttributeError: 'House' object has no attribute '_price'

The instance attribute was deleted successfully ?. When we try to access it again, an error is thrown because the attribute doesn't exist anymore.

? Some final Tips

You don't necessarily have to define all three methods for every property. You can define read-only properties by only including a getter method. You could also choose to define a getter and setter without a deleter.

If you think that an attribute should only be set when the instance is created or that it should only be modified internally within the class, you can omit the setter.

You can choose which methods to include depending on the context that you are working with.

? In Summary

  • You can define properties with the @property syntax, which is more compact and readable.
  • @vlastnosť je možné považovať za „pythonický“ spôsob definovania getrov, setterov a vymazávačov.
  • Definovaním vlastností môžete zmeniť internú implementáciu triedy bez toho, aby ste ovplyvnili program, takže môžete pridať getry, settery a deletery, ktoré pôsobia ako sprostredkovatelia „v zákulisí“, aby sa vyhli priamemu prístupu alebo úprave údajov.

Naozaj dúfam, že sa vám môj článok páčil a bol pre vás užitočný. Ak sa chcete dozvedieť viac informácií o vlastnostiach a objektovo orientovanom programovaní v Pythone, pozrite si môj online kurz, ktorý obsahuje viac ako 6 hodín video prednášok, cvičení kódovania a mini projektov.