CentAu
CentAu

Reputation: 11160

Python class interface or inheritance

I am fairly new to python and I am a bit confused about the correct design of interfaces. I want different implementations for a single class. AFAIK there are two ways to achieve this but I am not sure which one is the better way to go: First approach: Using class inheritance in one file: directory structure:

method
|--- __init__.py
|--- a.py

a.py:

class A(object):
    def do_sth(self):   pass

class A_imp1(A):
    def do_sth(self):   return 1

class A_imp2(A):
    def do_sth(self):   return 2

Usage:

from method.a import A_imp1, A_imp2
instance1 = A_imp1()
instance2 = A_imp2()
instance1.do_sth()
instance2.do_sth()

Second approach: Directory structure:

method
|--- __init__.py
|--- a_int.py
|--- a_1.py
|--- a_2.py

a_int.py:

class A_interface(object):
    def do_sth(self):   pass

a_1.py:

from a_int import A_interface
class A(A_interface):
        def do_sth(self): return 1

a_2.py

from a_int import A_interface
class A(A_interface):
    def do_sth(self): return 2

Usage:

from method.a_1 import A as A_1
from method.a_2 import A as A_2
instance1 = A_1.A()
instance2 = A_2.A()
instance1.do_sth()
instance2.do_sth()

Which one (if any) is the correct pythonic way for achieving this?

Upvotes: 2

Views: 3198

Answers (2)

Ashwini Chaudhary
Ashwini Chaudhary

Reputation: 250971

The correct way to implement an interface(Jump to section 4: Software interfaces in object-oriented languages) will be to use an abstract base class rather than a simple so that all the classes inheriting from it are enforced to implement their own methods:

from abc import ABCMeta, abstractmethod

class A(object):
    __metaclass__ = metaclass=ABCMeta

    @abstractmethod
    def do_sth(self):   pass

class A_imp1(A):
    def do_sth(self): return 1

class A_imp2(A):
    def do_sth(self): return 2

Now a user cannot initialize A on its own and any class inheriting from A, that doesn't defines its own do_sth, will fail to initialize as well:

>>> A()

Traceback (most recent call last):
  File "<pyshell#0>", line 1, in <module>
    A()
TypeError: Can't instantiate abstract class A with abstract methods do_sth


>>> class A_imp3(A):
    pass

>>> a = A_imp3()

Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    a = A_imp3()
TypeError: Can't instantiate abstract class A_imp3 with abstract methods do_sth

With that said, I think both of your approaches look fine to me. It's basically how you would like to manage your classes. The Python implementation of io module in Python 3 has everything defined in a single place.

Upvotes: 3

Ngenator
Ngenator

Reputation: 11259

Depends really, either are valid.

Say you're writing classes for employees, maybe you start off with

class Person(object):
    def __init__(self, name):
        self._name = name

    def get_name(self):
        return self._name

    def get_position(self):
        return 'Not Hired'

class Employee(Person):
    def get_position(self):
        return 'Employee'

class Manager(Employee):
    def __init__(self, *args, **kwargs):
        self.employees = set()
        super(Manager, self).__init__(*args, **kwargs)

    def get_position(self):
        return 'Manager'

    def manage(self, employee):
        self.employees.add(employee)

An employee will still have access to it's get_name method, but it will override the get_position method. Same thing for a manager.

Now, say you're writing a server

class BaseServer(object):
    def handle_request(self, request):
        raise NotImplementedError

class Server(BaseServer):
    def handle_request(self, request):
        ... do stuff ...

Here, you have an interface because you want to be sure any subclass will actually override your handle_request method.

Depending on what you're doing, either way could work.

Upvotes: 4

Related Questions