Help us understand the problem. What is going on with this article?

Pythonの継承とAbstract Base Class

More than 1 year has passed since last update.

pythonのバージョンを新しくして実行したところ、挙動が変わっていたので補足を末尾に追記しました(2019/1/6)

pythonで同じようなクラスを作ることになったので、Interfaceだったり抽象クラスだったりを使って実装してみたいと思って調べました。
で、調べてみたところ、Abstract Base Classというものが存在していることを知りました。
ふむふむ。
実は継承してクラスを作ったことがないので、それと比較してみました。

まずは、普通の継承。

Parent.py
class Parent(object):

    def __init__(self, name):
        self.name = name  # self.__nameだとChildから呼べない

    def explain(self):
        print(self.name)
Child.py
from Parent import *

class Child(Parent):

    def __init__(self, name, age):
        super().__init__(name)
        self.__age = age

    def hello(self):
        print('Hello ' + self.name, self.__age)

これをmain.pyから呼びます。

main.py
from Child import *

if __name__ == "__main__":
    parent = Parent('Bob')
    parent.explain()  # Bob

    child = Child('Bob Jr.', 6)
    child.explain()  # Bob Jr.
    child.hello()  # Hello Bob Jr. 6

コメントにもありますが、親クラスの属性がprivateだと、子クラスから参照できませんでした。
知らなかった・・・pythonにはprotectedな変数を作ることができないので、publicにする必要があります。

次はAbstract Base Classを使ってみます。

Abstract.py
from abc import *

class Abstract(object):
    __metaclass__ = ABCMeta

    def __init__(self, name):
        self.name = self.uppercase(name)  # self.__nameだとImplementから呼べないのは継承と同じ

    @classmethod
    @abstractmethod
    def uppercase(cls, name):  # __uppercase(cls, name):だと、Implementクラスで実装しても、Abstractクラスのメソッドが呼ばれる
        raise NotImplementedError()

    def explain(self):
        print(self.name)

    @abstractmethod
    def hello(self):
        raise NotImplementedError()

当然、上記のクラスを直接objectにすることはできません。
できないのですが、それはuppercaseメソッドがNotImplementedErrorとなっているからであって、実はそこを実装してしまえばobject自体は作れてしまいます。
で、実装クラスはこちら。

Implement.py
from Abstract import *

class Implement(Abstract):

    @classmethod
    def uppercase(cls, name):
        return name.upper() + ' by Impl'

    def hello(self):
        print('Hello ' + self.name)

これも、実はhelloメソッドを実装しなくても動きます。
その場合、helloメソッドを呼ぶと、Abstractクラスのhelloメソッドが呼ばれます(今回はNotImplementedErrorとなる)。

うーん、想像していた抽象クラスとは違う・・・(´・ω・`)
一応、PyCharmでコードを書くと、abstractmethodの実装がない場合は警告が出ますが、個人的にはAbstractクラスはobject作る時点でエラーになってほしいし、Implementクラスも、未実装のabstractmethodがある場合はエラーになってほしいのですが・・・。
一応、main.pyから呼ぶと、こんな感じです。

main.py
from Implement import *

if __name__ == "__main__":
    impl = Implement('John')
    impl.explain()  # JOHN by Impl
    impl.hello()  # Hello JOHN by Impl

でも、いい勉強になりました。

追記(2019/1/6)

python3.6.7 で再実行したところ、挙動が変わっていました。

from abc import ABCMeta, abstractmethod

class Abstract(metaclass=ABCMeta):

    def __init__(self, name):
        self.name = name

    @classmethod
    @abstractmethod
    def uppercase(cls, name):
        raise NotImplementedError()

    def explain(self):
        print(self.name)

    @abstractmethod
    def hello(self):
        raise NotImplementedError()


class Implement(Abstract):
    @classmethod
    def uppercase(cls, name):
        return name.upper() + ' by Impl'

class 作る際に __metaclass__ ではなく、直接 ABCMeta を metaclass と指定して継承すると、Abstract を object 化する際に、

abs = Abstract('Bob')

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Abstract with abstract methods hello, uppercase

となり、Implement も

Implement('Dan')

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Implement with abstract methods hello

となり、エラーが出るようになってました。
これでより安全にプログラミングができますね!

@ikeyan314 さんありがとうございます!!

ukisoft
まったり developer です。python と js を使うことが多いです。
rymansat
普段は宇宙開発に関わっていないサラリーマンが身近で誰でもできる宇宙開発を実現させることがリーマンサット・プロジェクト(Ryman Sat Project=rsp.)の目的です。キューブサットの開発をはじめ、宇宙を軸として様々なコミュニティやクリエイターとコラボレーションし、民間宇宙開発に関するネットワークを強化、拡張することを目指して活動しています。
https://www.rymansat.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした