6.1 継承は簡単
継承はオブジェクト指向プログラミング (OOP) の基本概念の1つで、1つのクラス (子クラスまたはサブクラスと呼ばれる) が、他のクラス (親クラスまたはスーパークラスと呼ばれる) のフィールドやメソッドを継承することを可能にするんだ。
こうすることで、より汎用的なクラスを作成したり、コードを再利用しやすくなり、コードの組織化と保守性が向上するよ。
継承を使う理由は?
例えば、クラスで何かコードを書こうとしているときに、既にプロジェクトに似たようなことをするクラスがあることを知ったとする。コードをコピーして自分のクラスで使うこともできるけど、本当のコピーじゃなくて、「コピーみたいなこと」をすることもできるよ。クラスを子クラスとして宣言すると、Pythonが親クラスの動作をそのクラスに追加してくれるんだ。
自然界の一部だと思って、犬を作りたいと思ってみて。細菌から10億年かけて犬を作るのと、2万年かけてオオカミを家畜化するの、どっちが早いかな?
基本的な継承の例
例えば、親クラスAnimal
があって、フィールドnameがあるとする:
class Animal:
def __init__(self, name):
self.name = name
これに対して、2つの子クラスDog
とCat
を作成し、それぞれにメソッドspeak
を追加したい:
class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"
上記の例では、子クラスDog
とCat
がAnimal
を継承し、メソッドspeak
を追加しているんだ。
クラスDog
:
Animal
からname
属性を継承する。- 犬特有の文字列を返すメソッド
speak
を追加する。
クラスCat
:
Animal
からname
属性を継承する。- 猫特有の文字列を返すメソッド
speak
を追加する。
完成したコードは次のようになるよ:
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"
dog = Dog("Buddy")
cat = Cat("Whiskers")
print(dog.speak()) # 出力: Buddy says Woof!
print(cat.speak()) # 出力: Whiskers says Meow!
この例では、Animal
は親クラスで、Dog
とCat
は子クラスだね。子クラスは親クラスAnimal
からname
属性と__init__
メソッドを継承しているけど、speak
というメソッドも追加しているよ。
6.2 継承の裏側
クラスに親クラスを追加すると、親クラスのコードが自分のクラスにコピーされたように思えることがあるよ。
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def __init__(self, name):
super().__init__(name) # 親クラスのコンストラクタを呼び出す
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def __init__(self, name):
super().__init__(name) # 親クラスのコンストラクタを呼び出す
def speak(self):
return f"{self.name} says Meow!"
dog = Dog("Buddy")
cat = Cat("Whiskers")
print(dog.speak()) # 出力: Buddy says Woof!
print(cat.speak()) # 出力: Whiskers says Meow!
これは厳密に言うと正確な説明ではないけれど、もし継承の概念を初めて聞くなら、しばらくこのくらいに考えておいてもいいよ。後で詳細を追加するからね。
6.3 継承の階層
複雑なクラスの大規模なグループを設計する際、継承の階層全体と向き合うことがあるかもしれない。
例えば、Animal
クラスがあるとすると、これはすべての動物の基底クラスだね:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError("Subclass must implement abstract method")
さらにメソッドspeak
を追加してるけど、抽象的な動物は話さないから、このメソッドはNotImplementedError
例外を投げているよ。それが標準のやり方なんだ。
次に、動物のカテゴリに対応する中間クラスを追加してみると: Mammal
は哺乳類で、Bird
は鳥類。
class Mammal(Animal):
def __init__(self, name, fur_color):
super().__init__(name) # 親クラスのコンストラクタを呼び出す
self.fur_color = fur_color
class Bird(Animal):
def __init__(self, name, wing_span):
super().__init__(name) # 親クラスのコンストラクタを呼び出す
self.wing_span = wing_span
def fly(self):
return f"{self.name} is flying with a wingspan of {self.wing_span} meters."
最後に、具体的な動物種のクラスが最終段階で現れるんだ:
class Dog(Mammal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Mammal):
def speak(self):
return f"{self.name} says Meow!"
class Parrot(Bird):
def speak(self):
return f"{self.name} says Squawk!"
通常、最終コードはこれらと一緒に動作するよ:
animals = [Dog("Buddy", "brown"), Cat("Whiskers", "white"), Parrot("Polly", 0.5)]
for animal in animals:
print(animal.speak())
print(f"{dog.name} has {dog.fur_color} fur.") # 出力: Buddy has brown fur.
print(f"{cat.name} has {cat.fur_color} fur.") # 出力: Whiskers has white fur.
print(parrot.fly()) # 出力: Polly is flying with a wingspan of 0.5 meters.
技術的には、数十の祖先を持つ階層を作成することに制限はないけれど、必要がない場合はシンプルさを維持するほうが良い。シンプルさの中に力があるんだよ。
GO TO FULL VERSION