pythonには抽象クラス、インターフェースがありませんでした。ですが、abc(abstract base class 抽象基底クラスの略)モジュールが追加されて、デコレータを使うことによって実現することが可能になりました。今回は、抽象クラスが実際にエラーを出す条件を知るために実験してみます。
抽象クラスはインスタンスを生成する際にエラーを出す
抽象クラスは実装して欲しい機能(メソッド)を強制するものです。pythonでは実装していない場合にエラーを出すのは、実装クラス(正確には未実装クラス)をインスタンス化する時だけです。そのため、抽象メソッドを実装していないクラスでも、クラスメソッドを使うためにアクセスするだけならエラーにはなりません。次の場合だと、抽象クラスとそのサブクラスをインスタンス生成するときだけにエラーが出ています。
from abc import *
class Abstract(object, metaclass=ABCMeta):
@classmethod
@abstractmethod
def hello(cls):
print("Abstract hello")
class Implement(Abstract):
pass
Abstract.hello()
# => Abstract hello
Implement.hello()
# => Abstract hello
# Abstract()
# TypeError: Can't instantiate abstract class Abstract with abstract methods hello
# Implement()
# TypeError: Can't instantiate abstract class Implement with abstract methods hello
もちろんインスタンス化する際には、抽象メソッドを実装(定義)しないといけません。他のメソッドを作ったとしても、それが実装したとは認められません。言い換えると、抽象クラスの作成者の基準をクリアしたことにはなりません。それを確認してみます。
from abc import *
class Abstract(object, metaclass=ABCMeta):
@classmethod
@abstractmethod
def hello(cls):
print("Abstract hello")
class Implement(Abstract):
@classmethod
def say(cls):
print("Implement class-method say")
def yeah(self):
print("Implement instance-method yeah")
Implement()
# => TypeError: Can't instantiate abstract class Implement with abstract methods hello
抽象クラスメソッドとインスタンスメソッドの区別はされない
抽象クラスのクラスメソッドを抽象メソッドとして定義します。そのサブクラスで同じ名前のメソッドをインスタンスメソッドとして定義した場合はエラーが起きません。おそらく、pythonは全てがオブジェクトで区別はないため、メソッドの種類ではなく名前だけを判断していると思われます。
from abc import *
class Abstract(object, metaclass=ABCMeta):
@classmethod
@abstractmethod
def hello(cls):
print("Abstract hello")
class Implement(Abstract):
def hello(self):
print("Implement hello")
impl = Implement()
impl.hello()
# => Implement hello
抽象クラスメソッドをインスタンスメソッドとして、抽象インスタンスメソッドをクラスメソッドとして定義してみます。今回起きたエラーはクラスメソッドhelloが存在しないことが原因です。サブクラスのhelloはインスタンスであり、親クラスのhelloは抽象メソッドのため、クラスメソッドはhelloは実装されていないのでエラーになります。クラスにアクセスするのは、エラーにならないけど、使えと命令されたメソッドがないからダメだよと言っているわけです。
from abc import *
class Abstract(object, metaclass=ABCMeta):
@classmethod
@abstractmethod
def hello(cls):
print("Abstract hello")
def say(self):
print("Abstract say")
class Implement(Abstract):
def hello(self):
print("Implement hello")
@classmethod
def say(cls):
print("Implement say")
impl = Implement()
impl.hello()
impl.say()
# =>
# Implement hello
# Implement say
# Implement.hello()
# hello() missing 1 required positional argument: 'self'
Implement.say()
# =>
# Implement say
どこまで実装を強制できるか
今回の実験で、クラスメソッドとインスタンスメソッドを区別して強制することはできないことがわかりました。エラーを自分で用意したり、abcモジュールのオプションなどで方法が用意されているかもしれません。ですが、通常通りの使い方では同じ名前のメソッドの実装までしか制限できないことを覚えておきましょう。