はじめに
僕はC#やJavaのような静的型付けのオブジェクト指向言語に慣れており、pythonを勉強中です。
C#やJavaにはinterfaceというのがあり、そのinterfaceを実装したさまざまなクラスのインスタンスを、すべて同じ型のオブジェクトのように取り扱うことができます。
同じようなことをpythonでもできないのかな、と思って調べてみました。
抽象基底クラス(ABC: Abstract Base Class)の定義
pythonはバージョン3で抽象基底クラス(ABC: Abstract Base Class)というものをサポートするようになったそうです。
これを利用することで、C#やJavaのインタフェースみたいなものを実現できるようです。
試してみましょう。
class AbstractHello(metaclass=ABCMeta):
@abstractmethod
def hello(self):
pass
このコードでは、メタクラスにABCMeta
を指定しています。これにより、抽象基底クラスAbstractHello
クラスを定義できます。
また、AbstractHello
クラス中のhello
メソッド定義には、@abstractmethod
というデコレータをつけます。これにより、AbstractHello
を継承するクラスは、hello
メソッドを必ずオーバーライドしなければならなくなります。
オーバーライドしなかった場合、そのクラスのインスタンスを生成した際にエラーとなります。
class WrongHello(AbstractHello):
pass
w = WrongHello()
# エラー
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-14-70ed5a2d6030> in <module>()
1 class WrongHello(AbstractHello):
2 pass
----> 3 w = WrongHello()
TypeError: Can't instantiate abstract class WrongHello with abstract methods hello
hello
メソッドを正しく実装した派生クラスを2つ定義してみましょう。
class HalfHello(AbstractHello):
def hello(self):
return 'Hello, world!'
class WideHello(AbstractHello):
def hello(self):
return 'Hello, world!'
helloes = [HalfHello(), WideHello()]
for h in helloes:
print(h.hello())
# 出力
Hello, world!
Hello, world!
このように、抽象基底クラスを利用することにより、C#やJavaのinterfaceと同じように、実装をサブクラスに隠蔽しつつ、クラスが持つメソッドのシグネチャを揃えることができます。
注意点
ただし、あくまでpythonは動的型付け言語であり、派生クラスの定義時に厳密な型チェックやメソッドのシグネチャのチェックは行っていないことに注意しましょう。
例えば、以下の例では、抽象基底クラスAbstractGreeting
では、文字列の引数name
を取るgreetinig
メソッドを定義しています。
一方で、AbstractGreeting
を継承するJapaneseGreeting
で定義したgreeting
メソッドは、引数name
を取りません。
それでも、エラーとならずにJapaneseGreeting
のインスタンスを生成、メソッド呼び出しをすることができます。
class AbstractGreeting(metaclass=ABCMeta):
@abstractmethod
def greeting(self, name):
pass
class EnglishGreeting(AbstractGreeting):
def greeting(self, name):
return 'Hello, %s' % name
class JapaneseGreeting(AbstractGreeting):
def greeting(self):
return 'こんにちは、%sさん' % 'John'
print(EnglishGreeting().greeting('John'))
print(JapaneseGreeting().greeting())
# 出力
Hello, John
こんにちは、Johnさん
C#やJavaであれば、JapaneseGreetingのようなクラス定義はコンパイルエラーとなりますが、pythonはそうはなりません。
あくまでも抽象基底クラスの継承者が自身の責任で正しくメソッドをオーバーライドする必要があります。
まとめ
- C#やJavaのinterfaceに似たものを定義するためには、抽象基底クラスを利用する。
- クラス定義に
SomeInterface<metaclass=ABCMeta)
と書き、メソッド定義に@abstractmethod
のデコレータをつければ良い。
- クラス定義に
- 抽象基底クラスを継承したクラスでは、
@abstractmethod
デコレータをつけたメソッドをすべてオーバーライドする必要がある。- ただし、メソッドのシグネチャが基底クラスと一致せずとも、呼び出さない限りはエラーとならないので注意する。