Reputation: 897
Suppose I have a two classes which cannot be linked via inheritance, but instances of which can be linked:
class model:
def __init__(self):
self._alpha = [0,1,2,3]
@property
def alpha(self):
return self._alpha
@alpha.setter
def alpha(self, inList):
self._alpha = inList
class solver:
def __init__(self, inModel):
self.model = inModel
Is there a way for me to define solver.alpha
to be model.alpha
without having to write out a bunch more boilerplate stuff:
class solver:
def __init__(self, inModel):
self.model = inModel
@property
def alpha(self):
return self.model.alpha
@alpha.setter
def alpha(self, inList):
self.modle.alpha = inList
Upvotes: 1
Views: 60
Reputation: 1570
Another sulotion is to "redirect" the attribute access from solver
to model
by overriding __getattr__
:
class solver:
def __init__(self, inModel):
self.model = inModel
def __getattr__(self, name):
# note that this only gets called if the normal attribute access failed
# so normal instance variables will still work.
return getattr(self.model, name)
Usage:
m = model()
s = solver(m)
print(s.alpha) # [0, 1, 2, 3]
This only works when getting attributes, in order to expand it to including setting (and also deleting) you need to override __setattr__
(and __delattr__
) but those don't have the nice feature of only getting called when the normal attribute access failes so you need to check for that:
def __setattr__(self, name, value):
try:
object.__setattr__(self, name, value) # try normal attribute access
except AttributeError:
setattr(self.model, name, value)
With this sulotion you don't need to list every attribute from model
you want to have on solver
, but you can't controll it either. Another limitation is that this will only work for one object, e.g. you can't have two models where some of the attributes are from one model and some attributes are from the other model.
Edit:
You might want to reflect the changes in the object's attribute access by overriding __dir__
. You'd want to merge the default dir
with self.model
's dir
. I did that by turning them into set
s and using the builtin set union
def __dir__(self):
return set(object.__dir__(self)) | set(dir(self.model))
Upvotes: 1
Reputation: 1570
You can write a descriptor class to automate the process:
class PathDescriptor:
def __init__(self, *paths):
self.paths = paths
def __get__(self, obj, objtype=None):
for path in self.paths:
obj = getattr(obj, path)
return obj
def __set__(self, obj, value):
for path in self.paths[:-1]:
obj = getattr(obj, path)
setattr(obj, self.paths[-1], value)
Example:
class solver:
def __init__(self, inModel):
self.model = inModel
alpha = PathDescriptor('model', 'alpha')
m = model()
s = solver(m)
print(s.alpha) # [0, 1, 2, 3]
Upvotes: 1