admin管理员组

文章数量:1290949

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?

  • I can't use inheritance for these classes
  • I'd like to avoid replicating the method() logic in B
  • I also tried setting __getattribute__ in B, but it seems it is not even called when resolving prop inside A

EDIT: 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?

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?

  • I can't use inheritance for these classes
  • I'd like to avoid replicating the method() logic in B
  • I also tried setting __getattribute__ in B, but it seems it is not even called when resolving prop inside A

EDIT: 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?

Share Improve this question edited Feb 14 at 11:40 Raquel asked Feb 13 at 22:57 RaquelRaquel 1218 bronze badges 1
  • I can't think of a good way to do this. A doesn't know anything about the wrapper. – Barmar Commented Feb 13 at 23:48
Add a comment  | 

1 Answer 1

Reset to default 2

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/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

Demo: https://ideone/uE3WHl

本文标签: pythonOverride property from wrapper classStack Overflow