Адаптируемый дескриптор в Python

Я хочу создать некоторый дескриптор класса, который возвращает прокси-объект. Прокси-объект при индексации извлекает элементы объекта и применяет к ним индекс. Тогда он возвращает сумму.

Например,

class NDArrayProxy:

    def __array__(self, dtype=None):
        retval = self[:]
        if dtype is not None:
            return retval.astype(dtype, copy=False)
        return retval


class ArraySumProxy(NDArrayProxy):

    def __init__(self, arrays):
        self.arrays = arrays

    @property
    def shape(self):
        return self.arrays[0].shape

    def __getitem__(self, indices):
        return np.sum([a[indices]
                       for a in self.arrays],
                      axis=0)

Это решение работало нормально, хотя у меня были фактические массивы в качестве переменных-членов:

class CompartmentCluster(Cluster):

    """
    Base class for cluster that manages evidence.
    """

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.variable_evidence = ArraySumProxy([])

class BasicEvidenceTargetCluster(CompartmentCluster):

    # This class variable creates a Python object named basic_in on the
    # class, which implements the descriptor protocol.

    def __init__(self,
                 *,
                 **kwargs):
        super().__init__(**kwargs)

        self.basic_in = np.zeros(self.size)
        self.variable_evidence.arrays.append(self.basic_in)

class ExplanationTargetCluster(CompartmentCluster):

    """
    These clusters accept explanation evidence.
    """

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.explanation_in = np.zeros(self.size)
        self.variable_evidence.arrays.append(self.explanation_in)

class X(BasicEvidenceTargetCluster, ExplanationTargetCluster):
    pass

Теперь я изменил свои массивы в дескрипторы Python (cluster_signal реализует протокол дескриптора, возвращающий пустой массив):

class CompartmentCluster(Cluster):

    """
    Base class for cluster that manages evidence.
    """

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.variable_evidence = ArraySumProxy([])

class BasicEvidenceTargetCluster(CompartmentCluster):

    # This class variable creates a Python object named basic_in on the
    # class, which implements the descriptor protocol.

    basic_in = cluster_signal(text="Basic (in)",
                              color='bright orange')

    def __init__(self,
                 *,
                 **kwargs):
        super().__init__(**kwargs)

        self.variable_evidence.arrays.append(self.basic_in)

class ExplanationTargetCluster(CompartmentCluster):

    """
    These clusters accept explanation evidence.
    """

    explanation_in = cluster_signal(text="Explanation (in)",
                                    color='bright yellow')

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.variable_evidence.arrays.append(self.explanation_in)

class X(BasicEvidenceTargetCluster, ExplanationTargetCluster):
    pass

Это не работает, потому что операторы добавления добавляют результат вызова дескриптора. Что мне нужно, это добавить либо связанный метод или аналогичный прокси. Какой самый лучший способ изменить мое решение? Короче говоря: переменные basic_in а также explanation_in мы numpy массивы. Теперь они дескрипторы. Я хотел бы разработать некоторую версию ArraySumProxy это работает с дескрипторами, а не требует фактических массивов.

1 ответ

Решение

Когда вы получаете доступ к дескриптору, он оценивается, и вы получаете только значение. Поскольку ваш дескриптор не всегда возвращает один и тот же объект (я полагаю, вы не можете избежать этого?), Вы не хотите получать доступ к дескриптору при инициализации прокси-сервера.

Самый простой способ избежать доступа к нему, это просто запомнить его имя, поэтому вместо:

self.variable_evidence.arrays.append(self.basic_in)

ты сделаешь:

self.variable_evidence.arrays.append((self, 'basic_in'))

Тогда, конечно, variable_evidence должен знать об этом и делать getattr(obj, name) чтобы получить к нему доступ.

Другой вариант - заставить дескриптор возвращать прокси-объект, который оценивается позже. Я не знаю, что вы делаете, но это может быть слишком много прокси для хорошего вкуса...

РЕДАКТИРОВАТЬ

Или... вы можете хранить получатель:

self.variable_evidence.arrays.append(lambda: self.basic_in)
Другие вопросы по тегам