Адаптируемый дескриптор в 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)