Python @property: Πώς να το χρησιμοποιήσετε και γιατί; - Προγραμματισμός

Σε αυτό το σεμινάριο, θα μάθετε για το Python @property decorator. ένας πύθωνος τρόπος για να χρησιμοποιείς getter και setter σε αντικειμενοστραφή προγραμματισμό.

Ο προγραμματισμός Python μας παρέχει έναν ενσωματωμένο @propertyδιακοσμητή ο οποίος κάνει τη χρήση των ρυθμιστών και των ρυθμιστών πολύ πιο εύκολη στον Προγραμματισμό με αντικείμενο.

Πριν αναφερθούμε σε λεπτομέρειες σχετικά με το τι @propertyείναι διακοσμητής, ας δημιουργήσουμε πρώτα μια διαίσθηση για το γιατί θα χρειαζόταν πρώτα.

Τάξη χωρίς Getters and Setter

Ας υποθέσουμε ότι αποφασίζουμε να κάνουμε μια τάξη που αποθηκεύει τη θερμοκρασία σε βαθμούς Κελσίου. Θα εφαρμόσει επίσης μια μέθοδο για τη μετατροπή της θερμοκρασίας σε βαθμούς Φαρενάιτ. Ένας τρόπος για να γίνει αυτό είναι ο εξής:

 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

Τα επιπλέον δεκαδικά ψηφία κατά τη μετατροπή σε Φαρενάιτ οφείλονται στο αριθμητικό σφάλμα κινητής υποδιαστολής. Για να μάθετε περισσότερα, επισκεφτείτε το Piththo Floating Point Arithmetic Error.

Κάθε φορά που εκχωρούμε ή ανακτούμε οποιοδήποτε χαρακτηριστικό αντικειμένου όπως temperatureφαίνεται παραπάνω, η Python το αναζητά στο ενσωματωμένο __dict__χαρακτηριστικό λεξικού του αντικειμένου .

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

Επομένως, man.temperatureγίνεται εσωτερικά man.__dict__('temperature').

Χρησιμοποιώντας Getters και Setter

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

Τώρα, ας χρησιμοποιήσουμε αυτήν την εφαρμογή:

 # 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 Traceback (τελευταία πρόσφατη κλήση): Αρχείο "", γραμμή 30, στο αρχείο "", γραμμή 16, στο set_temperature ValueError: Θερμοκρασία κάτω από -273.15 δεν είναι δυνατή.

Αυτή η ενημέρωση εφάρμοσε με επιτυχία τον νέο περιορισμό. Δεν επιτρέπεται πλέον να ρυθμίζουμε τη θερμοκρασία κάτω από -273,15 βαθμούς Κελσίου.

Σημείωση : Οι ιδιωτικές μεταβλητές δεν υπάρχουν στην Python. Υπάρχουν απλά κανόνες που πρέπει να ακολουθούνται. Η ίδια η γλώσσα δεν εφαρμόζει περιορισμούς.

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

Ωστόσο, το μεγαλύτερο πρόβλημα με την παραπάνω ενημέρωση είναι ότι όλα τα προγράμματα που υλοποιούνται προηγούμενη τάξη μας πρέπει να τροποποιήσουν τον κώδικά τους από το obj.temperatureνα obj.get_temperature()και όλες τις εκφράσεις όπως obj.temperature = valσε obj.set_temperature(val).

Αυτό το refactoring μπορεί να προκαλέσει προβλήματα ενώ αντιμετωπίζει εκατοντάδες χιλιάδες γραμμές κωδικών.

Συνολικά, η νέα μας ενημέρωση δεν ήταν συμβατή προς τα πίσω. Εδώ @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, σε τιμή set_temperatureError: Η θερμοκρασία κάτω από -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)

Αυτά τα δύο κομμάτια κωδικών είναι ισοδύναμα.

Οι προγραμματιστές εξοικειωμένοι με τους Python Decorators μπορούν να αναγνωρίσουν ότι η παραπάνω κατασκευή μπορεί να εφαρμοστεί ως διακοσμητής.

Δεν μπορούμε ακόμη και να ορίσουμε τα ονόματα get_temperatureκαι set_temperatureκαθώς είναι περιττά και να μολύνουν τον χώρο ονομάτων της τάξης.

Για αυτό, επαναχρησιμοποιούμε το temperatureόνομα ενώ καθορίζουμε τις λειτουργίες getter και setter. Ας δούμε πώς να το εφαρμόσουμε ως διακοσμητής:

 # 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 Ρύθμιση τιμής… Traceback (τελευταία τελευταία κλήση): Αρχείο "", γραμμή 29, στο Αρχείο "", γραμμή 4, στο __init__ Αρχείο "", γραμμή 18, σε θερμοκρασία ValueError: Η θερμοκρασία κάτω από -273 δεν είναι δυνατή

Η παραπάνω εφαρμογή είναι απλή και αποτελεσματική. Είναι ο προτεινόμενος τρόπος χρήσης property.

ενδιαφέροντα άρθρα...