I am trying to use use a list to reference a sequence of descriptors without success. There are a list of objects (_b
) defined by an external library (class A
) that I would like to access via descriptors (class Descriptor
). In the example below, b
is assigned a list of references to the descriptor, but when any item of the list is assigned a value, the reference to the descriptor is overwritten by the value instead of passing the value to the descriptor. I am apparently missing a fundamental behavior of descriptors even after reading several references and articles on descriptors.
class Descriptor(object):
def __init__(self, varname):
self.varname = varname
pass
def __get__(self, instance, owner):
print('get', self.varname)
#return getattr(getattr(instance, self.varname),"get")()
return instance.__dict__[self.varname].get()
def __set__(self, instance, value):
print('set', self.varname)
#getattr(getattr(instance, self.varname),"set")(value)
instance.__dict__[self.varname].set(value)
class A(object):
def __init__(self, value=None):
self.value = value
def get(self):
return self.value
def set(self, value):
self.value = value
class C(object):
print "root"
a = Descriptor('_a')
b = [Descriptor('_b[x]') for x in range(5)]
def __init__(self, val):
print "init"
self._a = A()
self.a = val
self._b = [A() for x in range(5)]
self.b[0] = 1
c = C(3)
d = C(4)
print c._a.get()
print c.a
print d._a.get()
print d.a
print c.b[0]
In my actual program the external library is a gui library which I would like to abstract so different interfaces can easily be exchanged. Several views in the gui contain columns of entry boxes (up to 40 per column) that correspond to lists in the program.
Also, which is the preferred method of accessing member function of the instance object passed into the descriptor: getattr
or __dict__
. __dict__
seems cleaner but I didn't know if there are any framework or usability issues in using it.
Any help on the question asked or suggestions of other approaches to meet my needs in the program are appreciated. Thanks.
Per millimoose's recommendation, the following list-like class seems to meet my needs. Any pitfalls with this method other than the descriptor being defined in the class root while the 'list descriptor' in the class __init__
as well as having to provide the class as an argument when initializing? Other list functions need to be added and special index behaviors such as negative indexes needs to be added.
class DescriptorList(object):
def __init__(self, owner, varname):
self.owner = owner
self.varname = varname
def __getitem__(self, index):
print('getitem', self.varname, index)
return getattr(getattr(self.owner, self.varname)[index],"get")()
def __setitem__(self, index, value):
print('setitem', self.varname, index)
getattr(getattr(self.owner, self.varname)[index],"set")(value)
class C(object):
a = Descriptor('_a')
def __init__(self, val):
self._a = A()
self.a = val
self._b = [A() for x in range(5)]
self.b = DescriptorList(self, '_b')
for i in range(5):
self.b[i] = i
c = C(3)
print [c.b[i] for i in range(5)]
Also, with DescriptorList
being instanced in C.__init__
, the code could be simplified so that the DescriptorList
uses the object itself instead of the object's name. Are there any advantages or disadvantages to this method?
class DescriptorList(object):
def __init__(self, var):
self.var = var
def __getitem__(self, index):
print('get', self.var, index)
return self.var[index].get()
def __setitem__(self, index, value):
print('set', self.var, index)
self.var[index].set(value)
class C(object):
a = Descriptor('_a')
def __init__(self, val):
self._a = A()
self.a = val
self._b = [A() for x in range(5)]
self.b = DescriptorList(self._b)
for i in range(5):
self.b[i] = i
Why are __get__
and __set__
handled differently from __getitem__
and __setitem__
?
__dict__
will fail for classes that use__slots__
, ornamedtuples
, and who knows what else. Basically, an arbitrary Python object is not guaranteed to have a__dict__
. – Adaptiveb = [Descriptor('_b[x]') for x in range(5)]
to do. descriptors only work when bound directly to a class, not put in a list which is bound to the class. Even if that did work, you don't usually name a variable_b[x]
– Galleywestb
, you don't want a descriptor. You need a list-like object that delegates to the appropriate methods ofA
instances, or creates them, etc. – Adaptivec.b[0] = 123
to ultimately call the methodA.set()
corresponding to a wrapper object forb[0]
. (Which of course can't be done with descriptors.) – Adaptive__dict__
. Are there any disadvantages or considerations usinggetattr
? – Resnickc.b[0]=123
ultimately calling A.set(). If this cannot be done with descriptors, is there another 'descriptor' like method that can be used or should I just access the appropriate member functions directly, e.g. A.set(), maybe creating a wrapper class to handle differences in the external libraries? – Resnicklist
that would automatically wrap / unwrap values in A. The best way to do this would be to implement theMutableSequence
ABC. As forgetattr
, I think it should be safe to use that one. It'd probably be a bug in Python if it couldn't retrieve an attribute that you can access directly. – Adaptive__get__
and__getitem__
were handled similarly. – Resnick