Цепочка событий в делере - PullRequest
       18

Цепочка событий в делере

2 голосов
/ 15 октября 2019

Предположим, у меня есть следующий класс с клиентом deleter для атрибута и для самого экземпляра:

class Hello:
    def __init__(self):
        self._is_open = None
    @property
    def is_open(self):
        return self._is_open
    @is_open.deleter
    def is_open(self):
        print("Using custom deleter!")
        del self._is_open
    def __delattr__(self, attr):
        print ('Deleting attr %s' % attr)
        super().__delattr__(attr)

И для его вызова:

>>> hello = Hello()
>>> del hello.is_open
Deleting attr is_open
Using custom deleter!
Deleting attr _is_open

Это выглядиткак он сначала вызывает __delattr__ на is_open, затем вызывает @is_open.deleter, затем вызывает __delattr__ на _is_open. Почему цепочка событий так работает для удалителей?

1 Ответ

2 голосов
/ 16 октября 2019

Python свойства являются дескрипторами . Они реализованы через протокол дескриптора .

Хук перебора данных __delattr__ имеет приоритет над протоколом дескриптора. Таким образом, если у вас определен пользовательский метод __delattr__, то он будет вызываться в предпочтении по отношению к средству удаления свойств.

Фактически, это реализация по умолчанию __delattr__, которая затем пойдет и вызовет дескрипторы. при необходимости вы можете проверить это, закомментировав строку, начинающуюся с super (вы должны увидеть, что средство удаления свойств теперь вообще не будет вызываться).

Вооружившись этим рассуждением, вы можете понять,цепочка событий, которая выглядит следующим образом:

Deleting attr is_open
# the del statement `del hello.is_open` is directly invoking Hello.__delattr__,
# passing in attr="is_open" as argument

# Now, the implementation of `Hello.__delattr__` calls
# `super().__delattr__(attr)`, passing along the argument attr="is_open", which
# then invokes a descriptor for that attribute (i.e. the function
# `Hello.is_open.fdel` is about to be called)

Using custom deleter!
# This comes from within the property (`Hello.is_open`) deleter.

Deleting attr _is_open
# The implementation of the `is_open` deleter actually uses another del
# statement, i.e. `del self._is_open`. This invokes again `Hello.__delattr__`,
# passing attr="_is_open" as an argument. However, this time there is no
# descriptor with the name `_is_open` present so an attribute gets deleted from
# the instance namespace instead. Note that the attribute `self._is_open` was
# there in `self.__dict__` already because it gets created during the __init__
# method when `hello = Hello()` is executed.

Важно отметить, что __delattr__ получил разные аргументы первый и второй раз: "is_open" сначала, затем "_is_open" секунда.

...