Search code examples
pythonpython-3.xinheritanceabstract-class

How to write to an abstract property in Python 3.4+


In Python 3.6, Let's say I have an abstract class MyAbstractClass

from abc import ABC, abstractmethod

class MyAbstractClass(ABC):

    @property
    @abstractmethod
    def myProperty(self):
        pass

and a class MyInstantiatableClass inherit from it. So how do I write to the property myProperty on instantiation of an object from this class? I'd like to be able to both set and get myProperty. Below doesn't work.

from MyAbstractClass import MyAbstractClass

class MyInstantiatableClass(MyAbstractClass):
    def __init__(self, desiredValueOfMyProperty):
        ????

    @myProperty.setter
    def myProperty(self, desiredValueOfMyProperty): # value coming from __init__
        self._myProperty = desiredValueOfMyProperty

And a main function, say,

from MyInstantiatableClass import MyInstantiatableClass

def main():
    MyInstantiatableClass(3) # 3 is the desiredValueOfMyProperty for this instantiation
    MyInstantiatableClass(5) # 5 is the desiredValueOfMyProperty for this instantiation

Solution

  • It seems there's a discrepancy here; using @property along with @abstractmethod doesn't seem to enforce classes that inherit from your abc to need to define both setter and getter. Using this:

    @property
    @abstractmethod
    def myProperty(self):
        pass
    
    @myProperty.setter
    @abstractmethod
    def myProperty(self):
        pass
    

    and then providing an implementation only for the getter in the class works and allows for instantiation:

    @property
    def myProperty(self):
        return self._myProperty
    

    This is due to the fact that only one name (myProperty) appears in the namespace of the ABC, when you override in the base class, you only need to define this one name.

    There's a way around that enforces it. You can create separate abstract methods and pass them on to property directly:

    class MyAbstractClass(ABC):
    
        @abstractmethod
        def getProperty(self):
            pass
    
        @abstractmethod
        def setProperty(self, val):
            pass
    
        myAbstractProperty = property(getProperty, setProperty)
    

    Providing an implementation for this abc now requires both getter and setter to have an implementation (both names that have been listed as abstractmethods in MyAbstractClass namespace need to have an implementation):

    class MyInstantiatableClass(MyAbstractClass):
    
        def getProperty(self):
            return self._Property
    
        def setProperty(self, val):
            self._Property = val
        myAbstractProperty = property(getProperty, setProperty)
    

    Implementing them is exactly the same as any old property. There's no difference there.