Σε αυτό το σεμινάριο, θα μάθετε για το 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 attributefset
is function to set value of the attributefdel
is function to delete the attributedoc
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
.