@Property Decorator i Python: Dens brugstilfælde, fordele og syntaks

? Mød egenskaber

Velkommen! I denne artikel lærer du, hvordan du arbejder med @propertydekoratøren i Python.

Du vil lære:

  • Fordelene ved at arbejde med egenskaber i Python.
  • Grundlæggende om dekoratørfunktioner: hvad de er, og hvordan de er relateret til @property.
  • Hvordan du kan bruge @property til at definere getters, setters og deleters.

1️⃣ Fordele ved ejendomme i Python

Lad os starte med en lille smule kontekst. Hvorfor bruger du egenskaber i Python?

Egenskaber kan betragtes som den "pythoniske" måde at arbejde med attributter på, fordi:

  • Syntaksen, der bruges til at definere egenskaber, er meget kortfattet og læsbar.
  • Du kan få adgang til instansattributter nøjagtigt som om de var offentlige attributter, mens du bruger "magien" hos formidlere (getters og settere) til at validere nye værdier og undgå at få adgang til eller ændre dataene direkte.
  • Ved at bruge @property kan du "genbruge" navnet på en ejendom for at undgå at oprette nye navne til getters, setters og deleters.

Disse fordele gør egenskaber til et virkelig fantastisk værktøj, der hjælper dig med at skrive mere kortfattet og læsbar kode. ?

2️⃣ Introduktion til dekoratører

En dekoratørfunktion er grundlæggende en funktion, der tilføjer ny funktionalitet til en funktion, der sendes som argument. At bruge en dekoratørfunktion er som at tilføje chokoladesprøjt til en is?. Det lader os tilføje ny funktionalitet til en eksisterende funktion uden at ændre den.

I eksemplet nedenfor kan du se, hvordan en typisk dekoratørfunktion ser ud i Python:

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

Lad os analysere disse elementer i detaljer:

  • Vi finder først dekoratørfunktionen def decorator(f)(dryssen ✨), der tager en funktion fsom et argument.
def decorator(f): def new_function(): print("Extra Functionality") f() return new_function
  • Denne dekoratørfunktion har en indlejret funktion new_function. Bemærk hvordan fkaldes inde for new_functionat opnå den samme funktionalitet, mens du tilføjer ny funktionalitet før funktionsopkaldet (vi kunne også tilføje ny funktionalitet efter funktionsopkaldet).
  • Dekoratorfunktionen returnerer selv den indlejrede funktion new_function.
  • Derefter (nedenfor) finder vi den funktion, der vil blive dekoreret (isen?) initial_function. Bemærk den meget ejendommelige syntaks ( @decorator) over funktionsoverskriften.
@decorator def initial_function(): print("Initial Functionality") initial_function()

Hvis vi kører koden, ser vi denne output:

Extra Functionality Initial Functionality

Bemærk hvordan dekoratørfunktionen kører, selvom vi kun ringer initial_function(). Dette er magien ved at tilføje @decorator?.

OteBemærkning: Generelt skriver vi @og erstatter navnet på dekoratørfunktionen efter @ -symbolet.

Jeg ved, at du måske spørger: hvordan er dette relateret til @property? @Property er en indbygget dekoratør til ejendomsfunktionen () i Python. Det bruges til at give "speciel" funktionalitet til bestemte metoder for at få dem til at fungere som getters, setter eller deleter, når vi definerer egenskaber i en klasse.

Nu hvor du er bekendt med dekoratører, lad os se et ægte scenario med brugen af ​​@property!

? Real-World Scenario: @property

Lad os sige, at denne klasse er en del af dit program. Du modellerer et hus med en Houseklasse (i øjeblikket har klassen kun defineret en prisinstansattribut ):

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

Denne instansattribut er offentlig, fordi dens navn ikke har en førende understregning. Da attributten i øjeblikket er offentlig, er det meget sandsynligt, at du og andre udviklere i dit team har fået adgang til og ændret attributten direkte i andre dele af programmet ved hjælp af punktnotation, som denne:

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

? Tip: obj repræsenterer en variabel, der refererer til en forekomst af House.

Indtil videre fungerer alt godt, ikke? Men lad os sige, at du bliver bedt om at gøre denne attribut beskyttet (ikke-offentlig) og validere den nye værdi, før du tildeler den . Specifikt skal du kontrollere, om værdien er en positiv float. Hvordan ville du gøre det? Lad os se.

Ændring af din kode

På dette tidspunkt, hvis du beslutter at tilføje getters og settere, vil du og dit team sandsynligvis få panik? Dette skyldes, at hver kodelinje, der får adgang til eller ændrer værdien af ​​attributten, skal ændres for at kalde henholdsvis getter eller setter. Ellers bryder koden ⚠️.

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

Men ... Ejendomme kommer til undsætning! Med @propertybehøver du og dit team ikke at ændre nogen af ​​disse linjer, fordi du er i stand til at tilføje getters og settere "bag kulisserne" uden at påvirke den syntaks, som du brugte til at få adgang til eller ændre attributten, da den var offentlig.

Fantastisk, ikke?  

Pro @ejendom: Syntaks og logik

Hvis du beslutter at bruge @property, vil din klasse se ud som eksemplet nedenfor:

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

Specifikt kan du definere tre metoder til en egenskab:

  • En getter - for at få adgang til værdien af ​​attributten.
  • En setter - for at indstille værdien af ​​attributten.
  • En sletning - for at slette instansattributten .

Prisen er nu "beskyttet"

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.
  • @property kan betragtes som den "pythoniske" måde at definere getters, setters og deleters på.
  • Ved at definere egenskaber kan du ændre den interne implementering af en klasse uden at påvirke programmet, så du kan tilføje getters, settere og deleter, der fungerer som mellemled "bag kulisserne" for at undgå at få adgang til eller ændre data direkte.

Jeg håber virkelig, du kunne lide min artikel og fandt det nyttigt. For at lære mere om egenskaber og objektorienteret programmering i Python, tjek mit online kursus, der inkluderer 6+ timers videoforelæsninger, kodningsøvelser og miniprojekter.