Search code examples
pythonclassexceptionarchitecturemember

Python interface asserting member variables are defined


I try to implement an interface in Python 3.6 (I know they don't exist in Python). Using the following minimal example:

import time

class ModuleInterface:
    # How often the module information is updated (int in seconds).
    interval = None
    def update(self):
        raise NotImplementedError

class HardDisk(ModuleInterface):
###############################################################################
     pass # First
###############################################################################
#     def update(self):        # Second
#         time.sleep(self.interval) # Second
###############################################################################

hd = HardDisk()
hd.update()

The code should yield a NotImplementedError for the first case. In the second case I would like to get a similar error but I don't know how to implement this correctly in Python. The idea of an interface is to yield an error if something is not defined. But interval is defined, which is why the second case will yield a TypeError. This is however not the kind of error I'd like to get. It would be perfect to assert for all members of the ModuleInterface that they must be defined by the inheriting class.


Solution

  • You are looking for the abc module. Exemple (Python 2.7 - you'll find py3 exemples in the 3.6 doc):

    import abc
    import time
    
    class ModuleInterface(object):
        __metaclass__ = abc.ABCMeta
    
        # How often the module information is updated (int in seconds).
        interval = abc.abstractproperty()
    
        @abc.abstractmethod
        def update(self):
            """ must be implemented """
    
    class WrongHardDisk(ModuleInterface):
        """ This one doesn't define `interval` 
            so it cannot be instanciated 
        """
        def update(self):
            time.sleep(self.interval)
    
    
    class HardDisk(ModuleInterface):
        interval = 5
        def update(self):
            time.sleep(self.interval)
    
    try:
        # Will raise a TypeError
        whd = WrongHardDisk()
    except Exception as e:
        print type(e), e
    
    # this one will work as expected
    hd = HardDisk()
    hd.update()
    

    AFAICT the only modification to make it work with Python 3.6 should be to replace this (untested):

    class ModuleInterface(object):
        __metaclass__ = abc.ABCMeta
        # ...
    

    with

    class ModuleInterface(metaclass=abc.ABCMeta):
        # ...