Reputation: 702
Consider the following code:
class A():
def __init__(self, thing):
self.thing = thing
def do_something(self):
if self.thing > 0:
print('thing is positive')
else:
print('thing is not positive')
def some_function(a):
if a.thing > 0:
print('this thing is positive')
else:
print('this thing is not positive')
class B(A):
@property
def thing(self):
return 0
@thing.setter
def thing(self, val):
pass
# Purposely don't want to override A.do_something
a = A(5)
print(a.thing) # 5
a.do_something() # thing is positive
some_function(a) # this thing is positive
isinstance(a, A) # True
b = B(5)
print(b.thing) # 0
b.do_something() # thing is not positive (!!! - not what we want - see below)
some_function(b) # this thing is not positive
isinstance(b, A) # True
Suppose that do_something
is a complicated function which we don't want to override. This could be because it is in an external package and we want to be able to keep using the latest version of this package containing A
without having to update B
each time. Now suppose that an outside function accesses a.thing
by referencing it directly. We want B
to extend A
so that this external function always sees b.thing == 0
. However, we want to do this without modifying the behaviour of internal methods. In the example above, we want to modify the behaviour of some_function
, but we do this at the cost of also changing the behaviour of the internal method b.do_something
.
The obvious way to fix this would be to have the external functions some_function
use a get_thing()
method. But if these external functions have been already written in another package modifying these is not possible.
Another way would be to have B
update the value of self.thing
before calling the parent class' method,
class B(A):
def __init__(self, thing):
self.thing = 0
self._thing = thing
def do_something(self):
self.thing = self._thing
rval = super().do_something()
self.thing = 0
return rval
however this seems clunky and if the developer of A
adds new methods, then B
would change the behaviour of these methods if it is not updated.
Is there a best practice on how to go about extending a class like this which allows use to override __getattribute__
if called by an external function, but without changing any internal behaviour?
Upvotes: 2
Views: 61
Reputation: 2312
I think you can set class B
like the following to achieve what you want:
class B:
def __init__(self, thing):
self.thing = 0
self._a = A(thing)
def __getattr__(self, name):
return getattr(self._a, name)
The full code is below:
class A:
def __init__(self, thing):
self.thing = thing
def do_something(self):
if self.thing > 0:
print('thing is positive')
else:
print('thing is not positive')
def some_function(a):
if a.thing > 0:
print('this thing is positive')
else:
print('this thing is not positive')
class B:
def __init__(self, thing):
self.thing = 0
self._a = A(thing)
def __getattr__(self, name):
return getattr(self._a, name)
if __name__ == '__main__':
a = A(5)
print(a.thing) # 5
a.do_something() # thing is positive
some_function(a) # this thing is positive
b = B(5)
print(b.thing) # 0
b.do_something() # thing is positive
some_function(b) # this thing is not positive
Upvotes: 1