Reputation: 854
Is there a way to hide or nest the Cat
attributes within Animal
while still being able to run the jump function? Specifically I would like to the output of vars(test)
to only be the age. I'm sure I could hard code which specific attributes to not output by defining a custom __str__
but I will have a lot of attributes for Cat/Animal
and I don't want to manually add an exception for each individual attribute. I also won't have access to the Cat
class.
def Jump():
print('Jumped!')
class Cat:
def __init__(self):
self.feet = 4
self.jump = Jump
class Animal(Cat):
def __init__(self):
Cat.__init__(self)
self.age = 3
test = Animal()
test.jump()
print(vars(test))
Output:
Jumped {'feet': 4, 'jump': , 'age': 3}
This is just code to illustrate what I'm trying to do. In reality Cat
represents a published python module and Animal
represents my custom one.
Upvotes: 0
Views: 45
Reputation: 365667
If you change your relationship so that Animal
no longer is-a Cat
, but instead has-a Cat
, then you're right, animal.jump()
will no longer work.
There are many ways around this. Given how odd this design already is, in so many different ways, I have no idea which is most appropriate, so I'll just list a whole bunch of them.
The first group are ways to explicitly delegate just jump
.
The "normal" way:
class Animal:
def __init__(self):
self.cat = Cat()
def jump(self):
return self.cat.jump()
Copy the per-instance function:
class Animal:
def __init__(self):
self.cat = Cat()
self.jump = self.cat.jump
Delegate to the per-instance function:
class Animal:
def __init__(self):
self.cat = Cat()
self.jump = lambda: self.cat.jump()
Per-instance bound method:
class Animal:
def __init__(self):
self.cat = Cat()
self.jump = (lambda self: self.cat.jump()).__get__(self)
Dynamic lookup:
class Animal:
def __init__(self):
self.cat = Cat()
def __getattr__(self, name):
if name == 'jump':
return getattr(self.cat, name)
raise AttributeError
Dynamic bound method generation:
class Animal:
def __init__(self):
self.cat = Cat()
def __getattr__(self, name):
if name == 'jump':
return (lambda self: getattr(self.cat, name)()).__get__(self)
raise AttributeError
Of course all of these only delegate jump
specifically. What if you wanted to delegate to all Cat
functions, methods, and maybe other attributes, without necessarily knowing what they are in advance? Well, it should be obvious how to adapt most of these, so I'll just show two.
Dynamic lookup:
class Animal:
def __init__(self):
self.cat = Cat()
def __getattr__(self, name):
return getattr(self.cat, name)
Semi-static inspection that does complicated reflection on the different possible kinds of things we might want to delegate:
class Animal:
def __init__(self):
self.cat = cat
for name, value in inspect.getmembers(self.cat):
if name.startswith('_'): continue
if inspect.ismethod(value):
value = (lambda self: value()).__get__(self)
elif callable(value):
value = lambda: value()
else:
value = copy.copy(value)
setattr(self, name, value)
Upvotes: 1