How to write to an abstract property in Python 3.4+

Question:

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
Asked By: Bora

||

Answers:

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.

For example, you can define the abstract getter, setter and deleter in Person abstract class, override them in Student class which extends Person abstract class as shown below. *@abstractmethod must be the innermost decorator otherwise error occurs:

from abc import ABC, abstractmethod

class Person(ABC):
    @property
    @abstractmethod # The innermost decorator
    def name(self): # Abstract getter
        pass

    @name.setter
    @abstractmethod # The innermost decorator
    def name(self, name): # Abstract setter
        pass

    @name.deleter
    @abstractmethod # The innermost decorator
    def name(self): # Abstract deleter
        pass

class Student(Person):
    def __init__(self, name):
        self._name = name
    
    @property
    def name(self): # Overrides abstract getter
        return self._name
    
    @name.setter
    def name(self, name): # Overrides abstract setter
        self._name = name
    
    @name.deleter
    def name(self): # Overrides abstract deleter 
        del self._name

Then, you can instantiate Student class and call the getter, setter and deleter as shown below:

obj = Student("John") # Instantiates "Student" class
print(obj.name) # Getter
obj.name = "Tom" # Setter
print(obj.name) # Getter
del obj.name # Deleter
print(hasattr(obj, "name"))

Output:

John
Tom
False

You can see my answer which explains more about abstract property.

Answered By: Kai – Kazuya Ito