How to call a property of the base class if this property is being overwritten in the derived class?

前端 未结 7 704
無奈伤痛
無奈伤痛 2020-11-27 14:13

I\'m changing some classes of mine from an extensive use of getters and setters to a more pythonic use of properties.

But now I\'m stuck because some of my previous

相关标签:
7条回答
  • Super should do the trick:

    return super().bar
    

    In Python 2.x you need to use the more verbose syntax:

    return super(FooBar, self).bar
    
    0 讨论(0)
  • 2020-11-27 15:01

    Some small improvements to Maxime's answer:

    • Using __class__ to avoid writing B. Note that self.__class__ is the runtime type of self, but __class__ without self is the name of the enclosing class definition. super() is a shorthand for super(__class__, self).
    • Using __set__ instead of fset. The latter is specific to propertys, but the former applies to all property-like objects (descriptors).
    class B(A):
        @property
        def prop(self):
            value = super().prop
            # do something with / modify value here
            return value
    
        @prop.setter
        def prop(self, value):
            # do something with / modify value here
            super(__class__, self.__class__).prop.__set__(self, value)
    
    0 讨论(0)
  • 2020-11-27 15:05

    You might think you could call the base class function which is called by property:

    class FooBar(Foo):
    
        @property
        def bar(self):
            # return the same value
            # as in the base class
            return Foo.bar(self)
    

    Though this is the most obvious thing to try I think - it does not work because bar is a property, not a callable.

    But a property is just an object, with a getter method to find the corresponding attribute:

    class FooBar(Foo):
    
        @property
        def bar(self):
            # return the same value
            # as in the base class
            return Foo.bar.fget(self)
    
    0 讨论(0)
  • 2020-11-27 15:10

    You can use the following template:

    class Parent():
        def __init__(self, value):
            self.__prop1 = value
    
        #getter
        @property
        def prop1(self):
            return self.__prop1
    
        #setter
        @prop1.setter
        def prop1(self, value):
            self.__prop1 = value
    
        #deleter
        @prop1.deleter
        def prop1(self):
            del self.__prop1
      
    class Child(Parent):
    
        #getter
        @property
        def prop1(self):
            return super(Child, Child).prop1.__get__(self)
    
        #setter
        @prop1.setter
        def prop1(self, value):
            super(Child, Child).prop1.__set__(self, value)
    
        #deleter
        @prop1.deleter
        def prop1(self):
            super(Child, Child).prop1.__delete__(self)
    

    Note! All of the property methods must be redefined together. If do not want to redefine all methods, use the following template instead:

    class Parent():
        def __init__(self, value):
            self.__prop1 = value
    
        #getter
        @property
        def prop1(self):
            return self.__prop1
    
        #setter
        @prop1.setter
        def prop1(self, value):
            self.__prop1 = value
    
        #deleter
        @prop1.deleter
        def prop1(self):
            del self.__prop1
    
    
    class Child(Parent):
    
        #getter
        @Parent.prop1.getter
        def prop1(self):
            return super(Child, Child).prop1.__get__(self)
    
        #setter
        @Parent.prop1.setter
        def prop1(self, value):
            super(Child, Child).prop1.__set__(self, value)
    
        #deleter
        @Parent.prop1.deleter
        def prop1(self):
            super(Child, Child).prop1.__delete__(self)
    
    0 讨论(0)
  • 2020-11-27 15:12

    There is an alternative using super that does not require to explicitly reference the base class name.

    Base class A:

    class A(object):
        def __init__(self):
            self._prop = None
    
        @property
        def prop(self):
            return self._prop
    
        @prop.setter
        def prop(self, value):
            self._prop = value
    
    class B(A):
        # we want to extend prop here
        pass
    

    In B, accessing the property getter of the parent class A:

    As others have already answered, it's:

    super(B, self).prop
    

    Or in Python 3:

    super().prop
    

    This returns the value returned by the getter of the property, not the getter itself but it's sufficient to extend the getter.

    In B, accessing the property setter of the parent class A:

    The best recommendation I've seen so far is the following:

    A.prop.fset(self, value)
    

    I believe this one is better:

    super(B, self.__class__).prop.fset(self, value)
    

    In this example both options are equivalent but using super has the advantage of being independent from the base classes of B. If B were to inherit from a C class also extending the property, you would not have to update B's code.

    Full code of B extending A's property:

    class B(A):
        @property
        def prop(self):
            value = super(B, self).prop
            # do something with / modify value here
            return value
    
        @prop.setter
        def prop(self, value):
            # do something with / modify value here
            super(B, self.__class__).prop.fset(self, value)
    

    One caveat:

    Unless your property doesn't have a setter, you have to define both the setter and the getter in B even if you only change the behaviour of one of them.

    0 讨论(0)
  • 2020-11-27 15:12
        class Base(object):
          def method(self):
            print "Base method was called"
    
        class Derived(Base):
          def method(self):
            super(Derived,self).method()
            print "Derived method was called"
    
        d = Derived()
        d.method()
    

    (that is unless I am missing something from your explanation)

    0 讨论(0)
提交回复
热议问题