Mik azok a Python-leírók?
A Python descriptor protokoll egy mód annak meghatározására, hogy mi történik, ha egy attribútumra hivatkoznak egy modellen. Lehetővé teszi az attribútumok hozzáférésének egyszerű kezelését:
- hozzáférést kaphat a
__get__
metódus implementálásával a Leíróban, ahol a__get__
hozzáfér az attribútumhoz. Visszaadja az attribútum értékét, vagy megemeli aAttributeError
kivételt, ha a kért attribútum nincs jelen. - állítsa be a hozzáférést a
__set__
metódus implementálásával a Leíróban, ahol a__set__
meghívásra kerül egy attribútum-hozzárendelési műveletben.None
. - törlési hozzáférés a
__delete__
metódus megvalósításával a Leíróban A__delete__
egy törlési műveletet vezérel. Visszatér:None
.
Mikor használhatjuk a Leírókat?
használhatjuk a Leírókat az attribútum értékének érvényesítésére az érték hozzárendelése előtt, például: vegyünk egy phone_number
attribútumot. a helyes telefonszám-formátum ellenőrzése szükséges, mielőtt értéket rendelne az attribútumhoz. a leíró lehetővé teszi a phone_number
reguláris kifejezéssel történő feldolgozását, és a formátum érvényesítését az attribútumhoz való hozzárendelés előtt.
Hogyan határozhatjuk meg és használhatjuk a Leírókat?
Tegyük fel, hogy van egy Employee
nevű osztályunk, amely egy attribútumot tartalmaz a munkavállaló fizetésére vonatkozóan, és ellenőriznünk kell, hogy a fizetés értéke szám-e, mielőtt hozzárendelnénk az értéket a fizetés attribútumhoz.
kétféleképpen határozhatunk meg egy leírót
- új osztály meghatározása
- "tulajdon" használatával
Próbáljunk meg egy leírót írni a fizetés attribútum értékének osztályok használatával történő érvényesítéséhez:
import numbers class NumberField: def __get__(self, obj, obj_type=None): return obj._salary def __set__(self, obj, value): if not isinstance(value, numbers.Number): raise TypeError(f"{value} is not a number") obj._salary = value def __delete__(self, obj): del obj._salary class Employee: salary = NumberField()
Teszteljük ezt a leírót:
In [2]: hesham = Employee() # NOTE: assignment calls __set__ in the descriptor In [3]: hesham.salary = 200 # NOTE: print calls __get__ in the descriptor In [4]: print(hesham.salary) # NOTE: del calls __delete__ in the descriptor In [5]: del hesham.salary
most próbáljunk meg érvénytelen értéket rendelni a salary
attribútumhoz
In [6]: wakita = Employee() # NOTE: wakita.salary = "two hundreds" --------------------------------------------------------------------------- TypeError Traceback (most recent call last) Input In [7], in <cell line: 2>() 1 # the following will raise a TypeError exception ----> 2 wakita.salary = "two hundreds" Input In [1], in NumberField.__set__(self, obj, value) 11 if not isinstance(value, numbers.Number): ---> 12 raise TypeError(f'{value} is not a number') 13 obj._salary = value TypeError: two hundreds is not a number
próbáljunk meg egy leírót írni a fizetés attribútum értékének érvényesítéséhez a property: használatával
import numbers class Employee: def get_salary(self): return self._salary def set_salary(self, value): if not isinstance(value, numbers.Number): raise TypeError(f'{value} is not a number') self._salary = value def delete_salary(self): del self._salary salary = property(get_salary, set_salary, delete_salary)
Teszteljük ezt a leírót:
In [3]: hesham = Employee() # NOTE: assignment calls set_salary method In [4]: hesham.salary = 200 # NOTE: print calls set_salary method In [5]: print(hesham.salary) 200 # NOTE: del calls delete_salary method In [6]: del hesham.salary
most próbáljunk meg érvénytelen értéket rendelni a salary
attribútumhoz
In [7]: wakita = Employee() In [8] # the following will raise a TypeError exception In [9]: wakita.salary = "200" --------------------------------------------------------------------------- TypeError Traceback (most recent call last) Input In [9], in <cell line: 1>() ----> 1 wakita.salary = "200" Input In [2], in Employee.set_salary(self, value) 5 def set_salary(self, value): 6 if not isinstance(value, numbers.Number): ----> 7 raise TypeError(f'{value} is not a number') 8 self._salary = value TypeError: 200 is not a number
Hogyan készíthetünk Leírót testreszabott attribútumnevekkel?
az előző NumberField
Leíróban az attribútum neve kemény kódolású _salary
. Amikor egy osztály leírókat használ, minden leírót tájékoztathat arról, hogy melyik változónevet használta.
A következő példában a Employee
osztálynak két leírópéldánya van, név és cím. Amikor a Employee
osztály definiálva van, visszahívást kezdeményez a StringField
-ban lévő __set_name__
számára, hogy a mezőneveket rögzíteni lehessen.
class StringField: def __set_name__(self, owner, name): self.attr_name = '_' + name def __get__(self, obj, obj_type=None): print(f'__get__ method attribute name: {self.attr_name}') value = getattr(obj, self.attr_name) return value def __set__(self, obj, value): print(f'__set__ method attribute name: {self.attr_name}, value: {value}') if not isinstance(value, str): raise TypeError(f'{value} is not a string value') setattr(obj, self.attr_name, value) def __delete__(self, obj): print(f'__delete__ method attribute name: {self.attr_name}') delattr(obj, self.attr_name) class Employee: # NOTE: the following descriptor is for name attribute name = StringField() # NOTE: the following descriptor is for address attribute address = StringField()
teszteljük leírónkat:
In [2]: emp1 = Employee() In [3]: emp1.name = "hesham" __set__ method attribute name: _name, value: hesham # NOTE: attribute name is `_name` In [4]: emp1.address = "Cairo, Egypt" __set__ method attribute name: _address, value: Cairo, Egypt # NOTE: attribute name is `_address` In [5]: print(emp1.name) __get__ method attribute name: _name hesham In [6]: print(emp1.address) __get__ method attribute name: _address Cairo, Egypt
MEGJEGYZÉS: Leírók használhatók az attribútumok hozzáférésének korlátozására, pl.: csak olvasható attribútum vagy csak írható attribútum
Következtetés
A Python-leírók hatékonyak és rugalmasak az attribútumkezeléshez, de óvatosan használja őket, hogy elkerülje az objektumok normál viselkedésének felülírásából eredő szükségtelen kódbonyolítást.