How to create abstract properties in python abstract classes
Until Python 3.3, you cannot nest @abstractmethod
and @property
.
Use @abstractproperty
to create abstract properties (docs).
from abc import ABCMeta, abstractmethod, abstractproperty
class Base(object):
# ...
@abstractproperty
def name(self):
pass
The code now raises the correct exception:
Traceback (most recent call last): File "foo.py", line 36, in b1 = Base_1('abc') TypeError: Can't instantiate abstract class Base_1 with abstract methods name
Using the @property
decorator in the abstract class (as recommended in the answer by James) works if you want the required instance level attributes to use the property decorator as well.
If you don't want to use the property decorator, you can use super()
. I ended up using something like the __post_init__()
from dataclasses and it gets the desired functionality for instance level attributes:
import abc
from typing import List
class Abstract(abc.ABC):
"""An ABC with required attributes.
Attributes:
attr0
attr1
"""
@abc.abstractmethod
def __init__(self):
"""Forces you to implement __init__ in 'Concrete'.
Make sure to call __post_init__() from inside 'Concrete'."""
def __post_init__(self):
self._has_required_attributes()
# You can also type check here if you want.
def _has_required_attributes(self):
req_attrs: List[str] = ['attr0', 'attr1']
for attr in req_attrs:
if not hasattr(self, attr):
raise AttributeError(f"Missing attribute: '{attr}'")
class Concrete(Abstract):
def __init__(self, attr0, attr1):
self.attr0 = attr0
self.attr1 = attr1
self.attr2 = "some value" # not required
super().__post_init__() # Enforces the attribute requirement.
Since Python 3.3 a bug was fixed meaning the property()
decorator is now correctly identified as abstract when applied to an abstract method.
Note: Order matters, you have to use @property
above @abstractmethod
Python 3.3+: (python docs):
from abc import ABC, abstractmethod
class C(ABC):
@property
@abstractmethod
def my_abstract_property(self):
...
Python 2: (python docs)
from abc import ABC, abstractproperty
class C(ABC):
@abstractproperty
def my_abstract_property(self):
...
Based on James answer above
def compatibleabstractproperty(func):
if sys.version_info > (3, 3):
return property(abstractmethod(func))
else:
return abstractproperty(func)
and use it as a decorator
@compatibleabstractproperty
def env(self):
raise NotImplementedError()