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 a AttributeError 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.