Pythonの抽象クラス(ABCmeta)を詳しく説明したい

# 0.はじめに

Pythonの抽象クラスが思いのほか特殊だったので
メタクラスの説明も踏まえて少しばかり丁寧に解説したい


# 1.ABCmetaの基本的な使い方

PythonのAbstract(抽象クラス)は少し特殊で、メタクラスと呼ばれるものに
ABCmetaを指定してクラスを定義する(メタクラスについては後ほど説明)

```python
from abc import ABC, ABCMeta, abstractmethod

class Person(metaclass = ABCMeta):
    pass
```

ちなみにABCクラスというのもあってこちらはmetaclassを指定せず
継承するだけなので基本こっちの方が分かりやすい

```python
class Person(ABC):
    pass
```

抽象化したいメソッドに@abstractmethodを付ける

```python
@abstractmethod
def greeting(self):
    pass
```

継承したクラスで実装する

```python
class Yamada(Person):

    def greeting(self):
        print("こんにちわ、山田です。")
```

もちろん実装しないで、インスタンス化するとエラー

```python
class Yamada(Person):
    pass

yamada = Yamada()
---------------------------------------------------------------------------------------------------------------
TypeError: Can't instantiate abstract class Yamada with abstract methods greeting

```

以上が、基本的な抽象クラスの使い方

## 1.1そもそもメタクラスとはなんなのか

ここから少しそれてメタクラスの話、興味なかったら飛ばしてください

メタクラスとは

>オブジェクト指向プログラミングにおいてメタクラスとは、インスタンスがクラスとなるクラスのことである。通常のクラスがそのインスタンスの振る舞いを定義するように、メタクラスはそのインスタンスであるクラスを、そして更にそのクラスのインスタンスの振る舞いを定義する。

<cite>【出典】: [メタクラス - wikipedia](https://ja.wikipedia.org/wiki/メタクラス)</cite>

とまあよくわからないので、実例を出すと
実は、Pythonの隠し機能(?)でtypeからクラスを動的に定義できてしまう

```python
def test_init(self):
    pass

Test_Class = type('Test_Class', (object, ), dict(__init__ = test_init, ))
```

もちろん、このクラスをインスタンス化できる

```python
Test_Class_instance = Test_Class()
print(type(Test_Class_instance))
-------------------------------------------------------------------------------------------------------------------
<class '__main__.Test_Class'>
```

ようするに先ほどtypeで生成したTest_Classは、インスタンスともクラスの定義ともいえる
これが、狭義的な意味でのメタクラスになる

驚きなのは、Pythonにおいて普段何気なく定義しているclassは
type型のインスタンスだったりするのだ

```python
class define_class:
    pass

print(type(define_class))
-------------------------------------------------------------------------------------------------------------------
<class 'type'>
```

結局のところ、Pythonはclassを定義すると内部で
<code>type('classname', ...)</code>が自動で呼び出されるという仕組みになっている
ということになる

この自動で呼び出せれるクラスを変えてしまえという
超強力な機能が\_\_metaclass\_\_ の正体で、
ABCmetaというのはtypeを継承したクラスということになる
[ABCmetaのソースコード](https://github.com/python/cpython/blob/3.8/Lib/abc.py)

ちなみに、metaclassは強力すぎて言語の仕様自体変えてしまう
黒魔術のため乱用しないほうがいいです

# 2.classmethod、staticmethod、propertyの抽象化

@abstractclassmethod、@abstractstaticmethod、@abstractproperty
がともに非推奨になってしまったので、デコレータを重ねる必要がある

### 抽象クラス

```python
class Person(ABC):

    @staticmethod
    @abstractmethod
    def age_fudging(age):
        pass

    @classmethod
    @abstractmethod
    def weight_fudging(cls, weight):
        pass

    @property
    @abstractmethod
    def age(self):
        pass

    @age.setter
    @abstractmethod
    def age(self, val):
        pass

    @property
    @abstractmethod
    def weight(self):
        pass

    @weight.setter
    @abstractmethod
    def weight(self, val):
        pass
```

### 実装

```python
class Yamada(Person):

    def __init__(self):
        self.__age = 30
        self.__weight = 120
    
    #10歳サバ読み
    @staticmethod
    def age_fudging(age):
        return age - 10
    
    #20kgサバ読み
    @classmethod
    def weight_fudging(cls, weight):
        return weight - 20
    
    @property
    def age(self):
        return Yamada.age_fudging(self.__age)
    
    @age.setter
    def age(self):
        return
    
    @property
    def weight(self):
        return self.weight_fudging(self.__weight)
    
    @weight.setter
    def weight(self):
        return

y = Yamada()
print("名前:山田 年齢:{0} 体重:{1}".format(y.age, y.weight))
-----------------------------------------------------------------------------------------------------------------
名前:山田 年齢:20 体重:100
```

多少長くなってしまったが、基本的な使い方は同じ
ただ、@abstractmethodが下に来るようにしないとエラーを吐くので注意

ちなみに、Pythonではメンバ変数の前に「__」を付けるとプライベート化できます

# 3.抽象クラスの多重継承

Pythonでは多重継承が使えるが、抽象クラスではどうでしょう

ご存じの通り、多重継承はいろいろと問題があるので(菱形継承問題、名前衝突)
基本的に多重継承は使わない方が無難だが、使う場合はMixinなクラスであることが求められる

## 3.1Mixinとは？

>mixin とはオブジェクト指向プログラミング言語において、
サブクラスによって継承されることにより機能を提供し、単体で動作することを意図しないクラスである。言語によっては、
その言語でクラスや継承と呼ぶものとは別のシステムとして mixin がある場合もある（#バリエーションの節で詳述）。


<cite>【出典】: [Mixin - wikipedia](https://ja.wikipedia.org/wiki/Mixin)</cite>

またWikipediaからの引用だが要するに
<b>継承しないとまともに動かないクラス</b>のことで
抽象クラスやインターフェイスもMixinなクラスといえる

ということで、猫と人間を継承した獣人クラスを作っていく

```python
class Cat(ABC):

    @abstractmethod
    def mew(self):
        pass
    
    #Personにもある
    @abstractmethod
    def sleep(self):
        pass

class Person(ABC):

    @abstractmethod
    def greeting(self):
        pass

    #Catにもある
    @abstractmethod
    def sleep(self):
        pass

class Therianthrope(Person, Cat):

    def greeting(self):
        print("こんにちわ")

    def mew(self):
        print("にゃー")
    
    def sleep(self):
        print("zzz…")

cat_human = Therianthrope()

cat_human.greeting()
cat_human.mew()
cat_human.sleep()
-----------------------------------------------------------------------------------------------------------------
こんにちわ
にゃー
zzz…
```

ABCmetaでも難なく動作することがわかる
今回は、sleepというメソッドを名前衝突させているが
継承元で実装されているわけではないので、問題がない

最後にPythonの公式サイトにも書いてある通り
メタクラス同士の衝突には十分注意したほうがいい

# 4.抽象クラスの多段継承

多段継承で懸念になるのはABCmetaは継承すると引き継ぐのか
言い換えると、Pythonは抽象クラスを継承すると抽象クラスに
なってしまうのかという点

結論から言うとABCmetaは引き継ぐし、抽象クラスを継承すると抽象クラスになります

```python
class Animal(metaclass=ABCMeta):
    pass

class Person(Animal):
    pass

class Yamada(Person):
    pass

print(type(Person))
print(type(Yamada))
-------------------------------------------------------------------------------------------------------------------
<class 'abc.ABCMeta'>
<class 'abc.ABCMeta'>
```

つまるところ抽象メソッドはどこかしらで
実装すればよいということになる

```python
class Animal(metaclass=ABCMeta):

    @abstractmethod
    def run(self):
        pass


class Person(Animal):
    pass

class Yamada(Person):

    def run(self):
        print("時速12km")

y = Yamada()
y.run()
-------------------------------------------------------------------------------------------------------------------
時速12km
```

# 5.仮引数に抽象クラスをとる

だいぶ長くなってしまったが、最後に抽象クラスのざっくりとした
Pythonでも使える使用法の一例を紹介

実は、Pythonでも仮引数、戻り値の型を指定することができるので
先ほど作った親クラスであり抽象クラスでもある「Person」を仮引数の型に指定した
fall_asleepという関数を作っていきましょう

```python
from abstract import Person
import time

def fall_asleep(p:Person):
    sleep_time = p.sleep()
    time.sleep(sleep_time)
    print("!")
```

```python
class Person(ABC):

    @abstractmethod
    def greeting(self):
        pass

    @abstractmethod
    def sleep(self) -> float:
        pass
```

抽象クラスやら型指定やらと、もはやPythonではなくなって来てますが
この様なテクニックは、Pythonで少し規模が大きいソフトフェアを作るとき
なんかに役に立ちます。

例えばfall_asleepを作っている人は、具象クラスの実態(YamadaなのかIkedaなのかYosidaなのか)を
気にせず(知らず)、またいくら変更しようともsleepという関数が要件さえ満たしてれば
完成させられるので、fall_asleepは具象クラスではなく抽象クラス(Person)に依存しているといえる

このことをアーキテクチャでは「<b>依存関係逆転の原則(DIP)</b>」なんて
言ったりします

DIPは以下の物が該当

* (変化しやすい)具象クラスを参照しない
* (変化しやすい)具象クラスを継承しない
* 具象関数をオーバーライドしない

結局のところ抽象クラスに依存したほうが、
強固なアーキテクチャを実現できるというわけです

```python
y = Yamada()
fall_asleep(y)
-------------------------------------------------------------------------------------------------------------------
zzz…
!
```