Το @property Decorator στο Python: Οι θήκες χρήσης, τα πλεονεκτήματα και η σύνταξη

? Γνωρίστε τις ιδιότητες

Καλως ΗΡΘΑΤΕ! Σε αυτό το άρθρο, θα μάθετε πώς να συνεργάζεστε με τον @propertyδιακοσμητή στο Python.

Θα μάθεις:

  • Τα πλεονεκτήματα της εργασίας με ακίνητα στην Python.
  • Τα βασικά των λειτουργιών του διακοσμητή: τι είναι και πώς σχετίζονται με το @property.
  • Πώς μπορείτε να χρησιμοποιήσετε το @property για να ορίσετε τους λήπτες, τους ρυθμιστές και τα διαγράμματα.

1️⃣ Πλεονεκτήματα των ιδιοτήτων στο Python

Ας ξεκινήσουμε με λίγο πλαίσιο. Γιατί θα χρησιμοποιούσατε ιδιότητες στο Python;

Τα ακίνητα μπορούν να θεωρηθούν ο "Πυθικός" τρόπος εργασίας με χαρακτηριστικά επειδή:

  • Η σύνταξη που χρησιμοποιείται για τον ορισμό των ιδιοτήτων είναι πολύ περιεκτική και ευανάγνωστη.
  • Μπορείτε να αποκτήσετε πρόσβαση σε χαρακτηριστικά παρουσίας ακριβώς σαν να ήταν δημόσια χαρακτηριστικά ενώ χρησιμοποιείτε τη «μαγεία» των διαμεσολαβητών (getters και setter) για να επικυρώσετε νέες τιμές και να αποφύγετε την άμεση πρόσβαση ή τροποποίηση των δεδομένων.
  • Χρησιμοποιώντας το @property, μπορείτε να "επαναχρησιμοποιήσετε" το όνομα μιας ιδιοκτησίας για να αποφύγετε τη δημιουργία νέων ονομάτων για τους λήπτες, τους ρυθμιστές και τα διαγράμματα.

Αυτά τα πλεονεκτήματα καθιστούν τις ιδιότητες ένα πραγματικά φοβερό εργαλείο για να σας βοηθήσουν να γράψετε πιο περιεκτικό και αναγνώσιμο κώδικα. ;

2️⃣ Εισαγωγή στους Διακοσμητές

Η συνάρτηση διακοσμητή είναι βασικά μια συνάρτηση που προσθέτει νέες λειτουργίες σε μια συνάρτηση που μεταβιβάζεται ως όρισμα. Η χρήση μιας διακόσμησης είναι σαν να προσθέτετε σοκολάτα σε ένα παγωτό; Μας επιτρέπει να προσθέσουμε νέες λειτουργίες σε μια υπάρχουσα λειτουργία χωρίς να την τροποποιήσουμε.

Στο παρακάτω παράδειγμα, μπορείτε να δείτε πώς φαίνεται μια τυπική λειτουργία διακοσμητή στο Python:

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

Ας αναλύσουμε λεπτομερώς αυτά τα στοιχεία:

  • Αρχικά βρίσκουμε τη συνάρτηση διακοσμητή def decorator(f)(τα ψεκάζει ✨) που παίρνει μια συνάρτηση fως επιχείρημα.
def decorator(f): def new_function(): print("Extra Functionality") f() return new_function
  • Αυτή η λειτουργία διακοσμητή έχει μια ένθετη λειτουργία, new_function. Παρατηρήστε πώς fκαλείται στο εσωτερικό new_functionγια να επιτύχει την ίδια λειτουργικότητα ενώ προσθέτει νέα λειτουργικότητα πριν από την κλήση λειτουργίας (θα μπορούσαμε επίσης να προσθέσουμε νέα λειτουργικότητα μετά την κλήση λειτουργίας)
  • Η ίδια η λειτουργία διακοσμητή επιστρέφει την ένθετη συνάρτηση new_function.
  • Στη συνέχεια (παρακάτω), βρίσκουμε τη λειτουργία που θα διακοσμηθεί (το παγωτό;) initial_function. Παρατηρήστε την πολύ περίεργη σύνταξη ( @decorator) πάνω από την κεφαλίδα της συνάρτησης.
@decorator def initial_function(): print("Initial Functionality") initial_function()

Εάν εκτελέσουμε τον κώδικα, βλέπουμε αυτήν την έξοδο:

Extra Functionality Initial Functionality

Παρατηρήστε πώς λειτουργεί η λειτουργία του διακοσμητή ακόμα κι αν καλούμε μόνο initial_function(). Αυτή είναι η μαγεία της προσθήκης @decorator;

?Σημείωση: Γενικά, θα γράφαμε@ , αντικαθιστώντας το όνομα της συνάρτησης διακοσμητή μετά το σύμβολο @.

Ξέρω ότι μπορεί να ρωτάτε: πώς σχετίζεται αυτό με το @property; Το @property είναι ένας ενσωματωμένος διακοσμητής για τη λειτουργία του ακινήτου () στο Python. Χρησιμοποιείται για να δώσει "ειδική" λειτουργικότητα σε ορισμένες μεθόδους για να τις κάνει να λειτουργούν ως getter, setters ή deleters όταν ορίζουμε ιδιότητες σε μια τάξη.

Τώρα που είστε εξοικειωμένοι με τους διακοσμητές, ας δούμε ένα πραγματικό σενάριο για τη χρήση του @property!

? Σενάριο πραγματικού κόσμου: @property

Ας πούμε ότι αυτή η τάξη είναι μέρος του προγράμματος σας. Κάνετε μοντελοποίηση ενός σπιτιού με μια Houseτάξη (προς το παρόν, η τάξη έχει μόνο ένα χαρακτηριστικό παράδειγμα τιμής )

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

Αυτό το χαρακτηριστικό εμφάνισης είναι δημόσιο επειδή το όνομά του δεν έχει κορυφαία υπογράμμιση. Δεδομένου ότι το χαρακτηριστικό είναι προς το παρόν δημόσιο, είναι πολύ πιθανό εσείς και άλλοι προγραμματιστές στην ομάδα σας να έχετε πρόσβαση και να τροποποιήσετε το χαρακτηριστικό απευθείας σε άλλα μέρη του προγράμματος χρησιμοποιώντας σημείωση σημείων, όπως αυτό:

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

? Συμβουλή: Το obj αντιπροσωπεύει μια μεταβλητή που αναφέρεται σε μια παρουσία House.

Μέχρι στιγμής όλα λειτουργούν υπέροχα, σωστά; Αλλά ας πούμε ότι θα σας ζητηθεί να κάνετε αυτό το χαρακτηριστικό προστατεύονται (μη δημόσια) και να επικυρωθεί η νέα τιμή πριν από την ανάθεση αυτή . Συγκεκριμένα, πρέπει να ελέγξετε αν η τιμή είναι θετική. Πώς θα το έκανες αυτό; Ας δούμε.

Αλλαγή του Κώδικα σας

Σε αυτό το σημείο, εάν αποφασίσετε να προσθέσετε getter και setter, εσείς και η ομάδα σας πιθανότατα θα πανικοβληθείτε; Αυτό συμβαίνει επειδή κάθε γραμμή κώδικα που αποκτά πρόσβαση ή τροποποιεί την τιμή του χαρακτηριστικού θα πρέπει να τροποποιηθεί για να καλέσει τον λήπτη ή τον ρυθμιστή, αντίστοιχα. Διαφορετικά, ο κωδικός θα σπάσει ⚠️.

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

Αλλά ... οι ιδιότητες έρχονται στη διάσωση! Με @property, εσείς και η ομάδα σας δεν θα χρειαστεί να τροποποιήσετε καμία από αυτές τις γραμμές, επειδή θα μπορείτε να προσθέσετε λήψεις και ρυθμιστές "πίσω από τα παρασκήνια" χωρίς να επηρεάσετε τη σύνταξη στην οποία χρησιμοποιήσατε για πρόσβαση ή τροποποίηση του χαρακτηριστικού όταν ήταν δημόσια.

Φοβερό, σωστά;  

? @property: Σύνταξη και λογική

Εάν αποφασίσετε να χρησιμοποιήσετε @property, η τάξη σας θα μοιάζει με το παρακάτω παράδειγμα:

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

Συγκεκριμένα, μπορείτε να ορίσετε τρεις μεθόδους για μια ιδιότητα:

  • A getter - για πρόσβαση στην τιμή του χαρακτηριστικού.
  • Ένας ρυθμιστής - για να ορίσετε την τιμή του χαρακτηριστικού.
  • Μια Deleter - για να διαγράψετε το χαρακτηριστικό παράδειγμα.

Η τιμή είναι τώρα "Προστατευμένη"

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 μπορεί να θεωρηθεί ως ο "πυθικός" τρόπος καθορισμού των λήψεων, των ρυθμιστών και των διαγραμτών.
  • Καθορίζοντας ιδιότητες, μπορείτε να αλλάξετε την εσωτερική εφαρμογή μιας κλάσης χωρίς να επηρεάσετε το πρόγραμμα, οπότε μπορείτε να προσθέσετε λήψεις, ρυθμιστές και διαγραφές που λειτουργούν ως ενδιάμεσοι "πίσω από τα παρασκήνια" για να αποφύγετε την άμεση πρόσβαση ή τροποποίηση των δεδομένων.

Ελπίζω πραγματικά ότι σας άρεσε το άρθρο μου και το βρήκα χρήσιμο. Για να μάθετε περισσότερα σχετικά με το Properties και τον αντικειμενοστρεφή προγραμματισμό στο Python, ρίξτε μια ματιά στο διαδικτυακό μου μάθημα, το οποίο περιλαμβάνει 6+ ώρες διαλέξεων βίντεο, ασκήσεις κωδικοποίησης και μίνι έργα.