I have a class A with a property prop and a method method that uses this property:
class A:
@property
def prop(self) -> int:
return 1
def method(self) -> int:
return self.prop * 2 # Uses self.prop
Then I have a wrapper class that tries to override this property like this:
class B:
def __init__(self, a: A):
self._a = a
@property
def prop(self) -> int:
return 2 # Override A's property
def __getattr__(self, attr):
return getattr(self._a, attr) # Delegate attribute access to A if attr not in B
When b.method() is called, I'd like the calculation to be done with B's prop, and get 4 as result. What is happening instead is that prop is resolving within A, and b.method() returns 2.
b = B(a)
b.method() # Returns 2. Wanted 4
Is there any way I can override the property prop in A, when used on A's code, when it is being wrapped by B?
__getattribute__
in B, but it seems it is not even called when resolving prop inside AEDIT:
I also want the property to be overridden for other properties too. So if I have other_prop
in A as follows:
class A:
@property
def prop(self) -> int:
return 1
@property
def other_prop(self) -> int:
return self.prop * 4 # Uses self.prop
def method(self) -> int:
return self.prop * 2 # Uses self.prop
I'd like b.other_prop to return 8
Any way to achieve this?
You can test if the attribute obtained from _a
is a method and re-bind the underlying function (accessible via the __func__
attribute) to the B
instance so that the method can have access to B
's properties:
from inspect import ismethod
class B:
def __getattr__(self, attr):
value = getattr(self._a, attr)
if ismethod(value):
value = value.__func__.__get__(self)
return value
Demo: https://ideone.com/jLJma8
Note that it is unnecessary for __getattr__
to test if attr in self.__dict__:
because __getattr__
is called only when an attribute name is not found in __dict__
in the first place.
EDIT: Similarly, if you need properties of _a
to be able to access the B
instance, you can test if the attribute obtained from _a
's type is a property and call the the property's getter function with the B
instance instead. So together with the method re-binding logics above, B.__getattr__
shall become:
class B:
def __getattr__(self, attr):
value = getattr(type(self._a), attr, None)
if isinstance(value, property):
return value.fget(self)
value = getattr(self._a, attr)
if ismethod(value):
value = value.__func__.__get__(self)
return value