10
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

pythonのabcモジュールの抽象メソッドは、クラスかインスタンスは区別しない。

Last updated at Posted at 2015-12-14

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モジュールのオプションなどで方法が用意されているかもしれません。ですが、通常通りの使い方では同じ名前のメソッドの実装までしか制限できないことを覚えておきましょう。

10
8
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
10
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?