はじめに
僕は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デコレータをつけたメソッドをすべてオーバーライドする必要がある。- ただし、メソッドのシグネチャが基底クラスと一致せずとも、呼び出さない限りはエラーとならないので注意する。