I want to create some kind of descriptor on a class that returns a proxy object. The proxy object, when indexed retrieves members of the object and applies the index to them. Then it returns the sum.
E.g.,
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)
This solution worked fine while I had actual arrays as member variables:
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
Now I've changed my arrays into Python descriptors (cluster_signal
implements the descriptor protocol returning a numpy array):
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
This doesn't work because the append statements append the result of the descriptor call. What I need is to append either a bound method or similar proxy. What's the nicest way to modify my solution? In short: The variables basic_in
and explanation_in
were numpy
arrays. They're now descriptors. I would like to develop some version of ArraySumProxy
that works with descriptors rather than requiring actual arrays.
Descriptors are Python objects that implement a method of the descriptor protocol, which gives you the ability to create objects that have special behavior when they're accessed as attributes of other objects.
There are two types of descriptors: data descriptors and non-data ones. If a descriptor implements both 1 __get__() and __set__() , it's called a data descriptor; otherwise is a non-data descriptor.
The __set__() method is invoked when the value is set to the attribute, and unlike the __get__() method, it returns nothing. It has two arguments apart from the descriptor object itself, i.e., the instance which is the same as the __get__() method and the value argument, which is the value you assign to the attribute.
The Cliff's Notes version: descriptors are a low-level mechanism that lets you hook into an object's attributes being accessed. Properties are a high-level application of this; that is, properties are implemented using descriptors.
When you access a descriptor, it is evaluated and you only get the value. Since your descriptor does not always return the same object (I guess you cannot avioid it?), you dont want to access the descriptor when you are initializing your proxy.
The simplest way to avoid accessing it, is to just remember its name, so instead of:
self.variable_evidence.arrays.append(self.basic_in)
you do:
self.variable_evidence.arrays.append((self, 'basic_in'))
Then, of course, variable_evidence
has to be aware of that and do getattr(obj, name)
to access it.
Another option is to make the descriptor return a proxy object which is evaluated later. I don't know what you are doing, but that might be too many proxies for good taste...
EDIT
Or... you can store the getter:
self.variable_evidence.arrays.append(lambda: self.basic_in)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With