pythonのバージョンを新しくして実行したところ、挙動が変わっていたので補足を末尾に追記しました(2019/1/6)
pythonで同じようなクラスを作ることになったので、Interfaceだったり抽象クラスだったりを使って実装してみたいと思って調べました。
で、調べてみたところ、Abstract Base Classというものが存在していることを知りました。
ふむふむ。
実は継承してクラスを作ったことがないので、それと比較してみました。
まずは、普通の継承。
class Parent(object):
def __init__(self, name):
self.name = name # self.__nameだとChildから呼べない
def explain(self):
print(self.name)
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から呼びます。
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を使ってみます。
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自体は作れてしまいます。
で、実装クラスはこちら。
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から呼ぶと、こんな感じです。
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 さんありがとうございます!!