Θα ήθελα να καταλάβω πώς λειτουργεί η ενσωματωμένη συνάρτηση property
. Αυτό που με μπερδεύει είναι ότι η property
μπορεί επίσης να χρησιμοποιηθεί ως διακοσμητής, αλλά δέχεται ορίσματα μόνο όταν χρησιμοποιείται ως ενσωματωμένη συνάρτηση και όχι όταν χρησιμοποιείται ως διακοσμητής.
Αυτό το παράδειγμα προέρχεται από την τεκμηρίωση:
class C(object):
def __init__(self):
self._x = None
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
Τα ορίσματα του property
'είναι τα getx
, setx
, delx
και μια συμβολοσειρά doc.
Στον παρακάτω κώδικα το property
χρησιμοποιείται ως διακοσμητής. Το αντικείμενό του είναι η συνάρτηση x
, αλλά στον παραπάνω κώδικα δεν υπάρχει θέση για συνάρτηση αντικειμένου στα ορίσματα.
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
Και, πώς δημιουργούνται οι διακοσμητές x.setter
και x.deleter
;
Έχω μπερδευτεί.
Η συνάρτηση property()
επιστρέφει ένα ειδικό αντικείμενο περιγραφής:
>>> property()
<property object at 0x10ff07940>
Είναι αυτό το αντικείμενο που έχει επιπλέον μεθόδους:
>>> property().getter
<built-in method getter of property object at 0x10ff07998>
>>> property().setter
<built-in method setter of property object at 0x10ff07940>
>>> property().deleter
<built-in method deleter of property object at 0x10ff07998>
Αυτές λειτουργούν ως διακοσμητές επίσης. Επιστρέφουν ένα νέο αντικείμενο ιδιοκτησίας:
>>> property().getter(None)
<property object at 0x10ff079f0>
που είναι αντίγραφο του παλιού αντικειμένου, αλλά με μία από τις συναρτήσεις να έχει αντικατασταθεί.
Θυμηθείτε, ότι η σύνταξη @decorator
είναι απλά συντακτική ζάχαρη- η σύνταξη:
@property
def foo(self): return self._foo
στην πραγματικότητα σημαίνει το ίδιο πράγμα με το
def foo(self): return self._foo
foo = property(foo)
οπότε η συνάρτηση foo
αντικαθίσταται από την property(foo)
, η οποία όπως είδαμε παραπάνω είναι ένα ειδικό αντικείμενο. Στη συνέχεια, όταν χρησιμοποιείτε την @foo.setter()
, αυτό που κάνετε είναι να καλείτε τη μέθοδο property().setter
που σας έδειξα παραπάνω, η οποία επιστρέφει ένα νέο αντίγραφο της ιδιότητας, αλλά αυτή τη φορά με τη συνάρτηση setter να έχει αντικατασταθεί με τη διακοσμημένη μέθοδο.
Η ακόλουθη ακολουθία δημιουργεί επίσης μια πλήρη ιδιότητα, χρησιμοποιώντας αυτές τις μεθόδους διακοσμητή.
Πρώτα δημιουργούμε μερικές συναρτήσεις και ένα αντικείμενο property
με μόνο έναν getter:
>>> def getter(self): print('Get!')
...
>>> def setter(self, value): print('Set to {!r}!'.format(value))
...
>>> def deleter(self): print('Delete!')
...
>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True
Στη συνέχεια χρησιμοποιούμε τη μέθοδο .setter()
για να προσθέσουμε έναν setter:
>>> prop = prop.setter(setter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is None
True
Τέλος, προσθέτουμε έναν διαγραφέα με τη μέθοδο .deleter()
:
>>> prop = prop.deleter(deleter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is deleter
True
Τέλος, το αντικείμενο property
ενεργεί ως αντικείμενο περιγραφής, οπότε διαθέτει τις μεθόδους .__get__()
, .__set__()
και .__delete__()
για να συνδεθεί με τη λήψη, τον ορισμό και τη διαγραφή χαρακτηριστικών της περίπτωσης:
>>> class Foo: pass
...
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!
Το Descriptor Howto περιλαμβάνει μια καθαρή Python υλοποίηση δείγματος του τύπου property()
:
class Property: "Emulate PyProperty_Type() in Objects/descrobject.c", >, def init(self, fget=None, fset=None, fdel=None, doc=None): self.fget = fget self.fset = fset self.fdel = fdel εάν το doc είναι None και το fget δεν είναι None: doc = fget.doc self.doc = doc >, def get(self, obj, objtype=None): if obj is None: return self if self.fget is None: raise AttributeError("unreadable attribute") return self.fget(obj) >, def set(self, obj, value): if self.fset is None: raise AttributeError("can't set attribute") self.fset(obj, value) >, def delete(self, obj): if self.fdel is None: raise AttributeError("can't delete attribute") self.fdel(obj) >, def getter(self, fget): return type(self)(fget, self.fset, self.fdel, self.doc) >, def setter(self, fset): return type(self)(self.fget, fset, self.fdel, self.doc) >, def deleter(self, fdel): return type(self)(self.fget, self.fset, fdel, self.doc)
Η τεκμηρίωση λέει ότι είναι απλά μια συντόμευση για τη δημιουργία ιδιοτήτων μόνο για ανάγνωση. Έτσι
@property
def x(self):
return self._x
είναι ισοδύναμο με
def getx(self):
return self._x
x = property(getx)
Το πρώτο μέρος είναι απλό:
@property
def x(self): ...
είναι το ίδιο με
def x(self): ...
x = property(x)
property
με έναν απλό getter.Το επόμενο βήμα θα ήταν να επεκτείνουμε αυτή την ιδιότητα με έναν setter και έναν deleter. Και αυτό συμβαίνει με τις κατάλληλες μεθόδους:
@x.setter
def x(self, value): ...
επιστρέφει μια νέα ιδιότητα η οποία κληρονομεί τα πάντα από την παλιά x
συν τον δεδομένο setter.
Ο x.deleter
λειτουργεί με τον ίδιο τρόπο.