Reputation: 11160
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
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
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