LoginSignup
34
21

More than 5 years have passed since last update.

Pythonの抽象クラスで色々試してみた

Last updated at Posted at 2017-01-24

はじめに

どうも、Javaが未だに頭から離れないPythonistaです。
なんかPython界隈のすごい人って、抽象クラスとかあんまり使わないよね。
動的型付け言語の自由さを謳歌してるよね。
俺も早くそうなりたいな。でもね、Java脳が許してくれないの。抽象クラス使いたくなるの。別に悪いことじゃないんだけどね。

あなたは本質を見ているか

急に真面目に話します。最近は便利な世の中で、ググれば大概の答えは出てきます。
Pythonで抽象クラスを使う(べきかどうかは別にして)にはabcモジュールを使えばいいといろんな所に書いています。ではそのabcモジュールがどんな機能を提供してくれているか、あなたは知っていますか?言われたままプログラム書いて、抽象クラス書いた気になってるだけじゃないですか?ちなみに私は何にも知りません。なので色々試して見ます。

実験

典型的な抽象クラスを書きます。頭にAbsって書いちゃう時点でJava脳だよね。

hoge.py
class AbsAnimal(metaclass=ABCMeta):
    @abstractmethod
    def eat():
        pass

tmp = AbsAnimal()
>>>TypeError: Can't instantiate abstract class AbsAnimal with abstract methods eat

てな感じで、インスタンスの生成をするとエラーが出ます。抽象メソッドを持つ抽象クラスはインスタンスの生成ができないとのこと。いいね、Javaっぽくて。
ここで抽象メソッドのデコレータを削除した下記のクラスのインスタンスを生成するとどうなるのか試してみる。

hoge.py
class AbsAnimal(metaclass=ABCMeta):
    def eat():
        pass

tmp = AbsAnimal()
tmp.eat()
(エラー発生せず)

あれなんですね、抽象メソッドを持ってこその抽象クラスだといいたいのか(当たり前)。
じゃあ逆にmetaclassを外した下記のクラスはどうなるのか。

hoge.py
class AbsAnimal:
        @abstractmethod
    def eat():
        pass

tmp = AbsAnimal()
tmp.eat()
(エラー発生せず)

あれなんですね、ABCMetaと@abstractmethodを一つのクラスに同時に使って、初めて意味があるということなんですね。ちなみにもっとガチガチに書こうと思ったら、「pass」じゃなくて「raise NotImplementedError()」って書いてもいいかもね。

次に試したのは抽象クラスを継承したクラスの動作。

hoge.py
class AbsAnimal(metaclass=ABCMeta):
    @abstractmethod
    def eat(self):
        pass

class Human(AbsAnimal):
        #何も実装してなければエラーになるので、間に合わせ
    def __init__(self):
        pass

tmp = Human()
>>>TypeError: Can't instantiate abstract class Human with abstract methods eat

予想通り。次にeatをきちんと実装してみる。

hoge.py
class AbsAnimal(metaclass=ABCMeta):
    @abstractmethod
    def eat(self):
        pass

class Human(AbsAnimal):
    def eat(self):
        print('おいしい!')

tmp = Human()
tmp.eat()
>>>おいしい!

これも予想通り。次にHumanにも抽象メソッドアノテーションを持ったメソッドを追加してみる

hoge.py
class AbsAnimal(metaclass=ABCMeta):
    @abstractmethod
    def eat(self):
        pass

class Human(AbsAnimal):
    def eat(self):
        print('おいしい!')

    @abstractmethod
    def traning(self):
        pass


tmp = Human()
>>>TypeError: Can't instantiate abstract class Human with abstract methods traning

これは予想外。Humanクラスには「metaclass=ABCMeta」を指定してないから「@abstractmethod」は無視されると思ってた。
どうやら「metaclassは単一継承では引き継がれる」というPythonの仕様があるらしく、その影響でこのような動作になるらしい。なるほど。

最後に

なまじ他言語のノウハウがあるばかりに、Pythonにもそれを強要してしまう所は早く治したい。
言語ごとに思想があるので、そこは尊重しなければ。

そんな弊社ですが、もし興味がありましたら、Qiitaの弊社技術者ブログトップページの下の方から連絡いただけましたら嬉しいです。お待ちしています!

34
21
0

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
34
21