LoginSignup
127
91

More than 3 years have passed since last update.

PythonでC#やJavaのinterfaceみたいなものを実現する

Last updated at Posted at 2018-04-03

はじめに

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
127
91