Abstract attributes in Python
Python has a built-in exception for this, though you won't encounter the exception until runtime.
class Base(object):
@property
def path(self):
raise NotImplementedError
class SubClass(Base):
path = 'blah'
Abstract attribute (not property)?
If you really want to enforce that a subclass define a given attribute, you can use metaclasses:
class AbstractFooMeta(type):
def __call__(cls, *args, **kwargs):
"""Called when you call Foo(*args, **kwargs) """
obj = type.__call__(cls, *args, **kwargs)
obj.check_bar()
return obj
class AbstractFoo(object):
__metaclass__ = AbstractFooMeta
bar = None
def check_bar(self):
if self.bar is None:
raise NotImplementedError('Subclasses must define bar')
class GoodFoo(AbstractFoo):
def __init__(self):
self.bar = 3
class BadFoo(AbstractFoo):
def __init__(self):
pass
Basically the meta class redefine __call__
to make sure check_bar
is called after the init on an instance.
GoodFoo() # ok
BadFoo () # yield NotImplementedError
How to create an abstract attribute for an abstract class in Python?
The way to enforce this is not with abstractmethod
, but via __init_subclass__
.
class DataFilter:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
try:
cls.filter_name
except AttributeError:
raise TypeError("Can't instantiate class DataFilter without name 'filter_name'")
You can perform additional tests on the value of cls.filter_name
if it is present.
How to create an abstract class attribute (potentially read-only)
I came up with a solution based on those posted earlier. (Thank you @Daniel Roseman and @martineau)
I created a metaclass called ABCAMeta (the last 'A' stands for 'Attributes').
The class has two ways of working.
A class which just uses ABCAMeta as a metaclass must have a property called
required_attributes
which should contain a list of the names of all the attributes you want to require on future subclasses of that classA class whose parent's metaclass is ABCAMeta must have all the required attributes specified by its parent class(es).
For example:
class AbstractFoo(metaclass=ABCAMeta):
required_attributes = ['force_this']
class RealFoo(AbstractFoo):
pass
will throw an error:
NameError: Class 'RealFoo' has not implemented the following attributes: 'force_this'
Exactly how I wanted.
from abc import ABCMeta
class NoRequirements(RuntimeError):
def __init__(self, message):
RuntimeError.__init__(self, message)
class ABCAMeta(ABCMeta):
def __init__(mcls, name, bases, namespace):
ABCMeta.__init__(mcls, name, bases, namespace)
def __new__(mcls, name, bases, namespace):
def get_requirements(c):
"""c is a class that should have a 'required_attributes' attribute
this function will get that list of required attributes or
raise a NoRequirements error if it doesn't find one.
"""
if hasattr(c, 'required_attributes'):
return c.required_attributes
else:
raise NoRequirements(f"Class '{c.__name__}' has no 'required_attributes' property")
cls = super().__new__(mcls, name, bases, namespace)
# true if no parents of the class being created have ABCAMeta as their metaclass
basic_metaclass = True
# list of attributes the class being created must implement
# should stay empty if basic_metaclass stays True
reqs = []
for parent in bases:
parent_meta = type(parent)
if parent_meta==ABCAMeta:
# the class being created has a parent whose metaclass is ABCAMeta
# the class being created must contain the requirements of the parent class
basic_metaclass=False
try:
reqs.extend(get_requirements(parent))
except NoRequirements:
raise
# will force subclasses of the created class to define
# the attributes listed in the required_attributes attribute of the created class
if basic_metaclass:
get_requirements(cls) # just want it to raise an error if it doesn't have the attributes
else:
missingreqs = []
for req in reqs:
if not hasattr(cls, req):
missingreqs.append(req)
if len(missingreqs)!=0:
raise NameError(f"Class '{cls.__name__}' has not implemented the following attributes: {str(missingreqs)[1:-1]}")
return cls
Any suggestions for improvement are welcome in the comments.
How to create abstract properties in python abstract classes?
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):
...
Python Abstract Attribute
You're expecting each child class to have self.requirements
right? So change the following code to this.
class Event(object):
@property
def requirements(self):
try:
return self._requirements
except AttributeError:
raise NotImplementedError('subclasses must have requirements')
That way it will return self.requirements. If self.requirements hasn't been implemented by the child class it will raise a not implemented error.
EDIT: Updated return to avoid never-ending loop.
Related Topics
How to Create a Datetime in Python from Milliseconds
How to Set the Figure Title and Axes Labels Font Size in Matplotlib
How to Pass Extra Arguments to a Python Decorator
Subprocess: Deleting Child Processes in Windows
How to Keep Index When Using Pandas Merge
Use .Corr to Get the Correlation Between Two Columns
Correct Way to Define Class Variables in Python
Difference Between Returns and Printing in Python
How to Read Hdf5 Files in Python
How to Use SQL Parameters with Python
Removing Duplicates from Dictionary
Python Ctypes Issue on Different Oses
Import Error: No Module Named Numpy
Modules Are Installed Using Pip on Osx But Not Found When Importing
Functions That Help to Understand JSON(Dict) Structure
Multiprocessing in Python - Sharing Large Object (E.G. Pandas Dataframe) Between Multiple Processes