Pythonのメソッドについて正しく理解する

# はじめに
本記事はPython初心者から中級者になることを目的として、Pythonの各メソッドについてまとめています。

![メソッド.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/6b2a94b4-c55d-4204-8e77-094f40fd4af3.png)

## 高階関数
高階関数は引数として渡された関数を呼び出し、処理結果を戻り値として関数を返す関数のことです。

前提として高階関数の理解は重要です。高階関数を理解することでPythonでのデコレータの恩恵や、他のプログラミング言語などのコールバック関数の理解が深まると思います。

高階関数は全てのプログラミング言語で厳密に定義されている訳ではありません。しかし、サポートされていない言語においても標準の機能やライブラリを使用することで、高階関数を模倣することができます。

## デコレータ
デコレータの目的は関数のまわりで繰り返されるコードを汎用化して共通のコードに置き換えることです。

Pythonではデコレートしたい関数の前に、@高階関数を書くことで関数を置き換えることができます。デコレータのメリットは関数で処理した結果を変数に入れて使うなど、高階関数を駆使した複雑な処理をしなくて済むため、コードがスッキリします。

留意事項としてはデコレータされたメソッドの`__doc__`や`__name__`などの特殊属性は失われます。

以下はメソッド毎にログの処理を書くとコード量が増えるため、ログの処理をデコレートする場合のサンプルです。

```python
import logging
import traceback

logging.basicConfig(level=logging.DEBUG)

def logger(func):
    def inner(*args):
        _functions= {"file_name":func.__name__}
        print("step2 高階関数に渡す個別関数が呼ばれる")
        try:
            logging.info(str(_functions["file_name"]) + " 処理開始" + " 引数:" + str(args))
            return func(*args) 
        except:
            e = traceback.format_exc()
            logging.error(e)

    print("step1 高階関数の処理開始")
    return inner

@logger
def sum_count(a, b):
    print("step3 高階関数から呼ばれて処理結果を返す")
    return a + b
    
print(sum_count(1, 2))
```

実行すると以下の通りです。

```console
step1 高階関数の処理開始
step2 高階関数に渡す個別関数が呼ばれる
INFO:root:sum_count 処理開始 引数:(1, 2)
step3 高階関数から呼ばれたので処理結果を返す
3
```


## メソッド
メソッドはクラス属性として格納される関数のことです。

Pythonを深く知るためにはオブジェクト指向の理解が必要です。

Pythonの全てのクラスはObjectクラスを始祖としているため、Objectクラスから継承したメソッドを使用することができます。また、Pythonのオブジェクトは特殊な組み込み属性（アトリビュート）が定義されます。この組み込み属性（アトリビュート）を使用することでクラスやインスタンスの情報が取得できます。

| アトリビュート | 意味 |
|:-----------|:------------|
| \_\_name__       | クラス、関数、メソッド、デスクリプタ、ジェネレータインスタンスの名前        |
| \_\_module__       | クラスが定義されているモジュールの名前        |
| \_\_dict__       | オブジェクトの (書き込み可能な) 属性を保存するために使われる辞書またはその他のマッピングオブジェクト        |
| \_\_bases__       | クラスオブジェクトの基底クラスのタプル       |

dir関数を使用することでオブジェクトが持つ全ての属性を調べることができます。
引数がない場合、現在のローカルスコープにある名前のリストを返します。引数がある場合、そのオブジェクトの有効な属性のリストを返そうと試みます。

- Human.py

```python
from dataclasses import dataclass

@dataclass
class Human:
    sex: str = ""
    age: int = 0
    name: str = ""

man = Human(sex="men", name="alex")

print(dir(man))
```

dataclassで作成したクラスです。
実行するとオブジェクトの有効な属性がされます。また、インスタンスに定義したアトリビュートが確認できます。

```console
['__annotations__', '__class__', '__dataclass_fields__', '__dataclass_params__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'sex']
```


### 静的メソッド
静的メソッドはクラスで定義される共通関数のことです。

使い方はクラスの中で静的メソッドとして定義したいメソッドに対して、`@staticmethod`でデコレートします。

特徴としてクラスから直接実行できるため、インスタンスの作成が不要です。
つまり、インスタンスの影響を受けることがありません。

用途としてはクラス本体から関数を参照する必要があり、かつ、インスタンスメソッドに自動変換されるのを避けたいケースで必要になります。

静的メソッドのメリットはクラスから直接実行できるため、スピードが早いことです。
またインスタンス化を行わないため、微小ながらCPUやメモリ等のリソースを軽減できます。

以下は静的メソッドの使用例です。

- total_count.py

```python
class total_count:
    def __init__(self):
        pass

    def add_count(self, sales):
        return sales

    @staticmethod
    def sum_count(*args):
        return sum(args)

shop_a = total_count()
shop_b = total_count()
shop_c = total_count()

sales_a = shop_a.add_count(1000)
sales_b = shop_b.add_count(2000)
sales_c = shop_c.add_count(3000)

day_total = total_count.sum_count(sales_a, sales_b, sales_c)

print("売り上げ(A店):" + str(sales_a))
print("売り上げ(B店):" + str(sales_b))
print("売り上げ(C店):" + str(sales_c))
print("-------------------------")
print("売り上げ(全店):" + str(day_total))
```

実行するとインスタンス化したオブジェクトから各店舗の売り上げを取得し、最後に静的メソッドを呼び出して合計値を集計します。

```console
売り上げ(A店):1000
売り上げ(B店):2000
売り上げ(C店):3000
-------------------------
売り上げ(全店):6000
```

### クラスメソッド
クラスメソッドはクラスにバインドされるメソッドです。

使い方はクラスの中でクラスメソッドとして定義したいメソッドに対して、`@classmethod`でデコレートします。

単純にクラスから呼び出すという点では静的メソッドと同じですが、クラスメソッドは第一引数としてクラスをとります。また、クラス変数へアクセスすることができます。用途としてはファクトリメソッドを作成するときに効果を発揮すると思います。

上記静的メソッドで使用したコードを基に、クラスメソッドを使用した例を以下に記載します。

- total_count.py

```python
class total_count:
    capital = 5000

    def __init__(self):
        pass

    def add_count(self, sales):
        return sales

    @classmethod
    def sum_count(cls, *args):
        return sum(args)

shop_a = total_count()
shop_b = total_count()
shop_c = total_count()

sales_a = shop_a.add_count(1000)
sales_b = shop_b.add_count(2000)
sales_c = shop_c.add_count(-3000)

day_total = total_count.sum_count(sales_a, sales_b, sales_c)
capital_total = day_total + total_count.capital

print("売り上げ(A店):" + str(sales_a))
print("売り上げ(B店):" + str(sales_b))
print("売り上げ(C店):" + str(sales_c))
print("-------------------------")
print("売り上げ(全店):" + str(day_total))
print("資本:" + str(capital_total))
```

実行するとインスタンス化したオブジェクトの値と、クラス変数を足すことにより、各店舗の売り上げと会社の資本を足すという資金繰りを表現しています。

```console
売り上げ(A店):1000
売り上げ(B店):2000
売り上げ(C店):-3000
-------------------------
売り上げ(全店):0
資本:5000
```

この様にクラス変数を利用して動的に振る舞いをコントロールすることができる点は、クラスメソッドの良いところだと私は思います。

### 抽象メソッド
pythonではabcモジュール（Abstract Base Classes）を利用することで抽象基底クラスを定義することができます。

使い方は抽象メソッドとして定義したいメソッドに対して、`@abc.abstractmethod`でデコレートします。
（※）抽象メソッドを使用するためにはabcモジュールのインポートが必要です。

- 抽象クラス

```python
import abc

class Animal(object, metaclass=abc.ABCMeta):

    @abc.abstractclassmethod
    def cry(self):
        pass

Animal()
```

これを実行するとエラーになります。

```console
Traceback (most recent call last):
  File "test_fact.py", line 8, in <module>
    Animal()
TypeError: Can't instantiate abstract class Animal with abstract methods cry
```

クラスから直接メソッドを実行することができません。
言い方を変えると、クラスのメソッドを変更することができません。

そのため、クラスからインスタンスを作成して実行します。

- 具象クラス

```python
class Dog(Animal):

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

    def cry(self):
        return "wan" 

d = Dog('hachi')
print(d.cry())
```

インスタンスの実行結果になります。

```console
wan
```

なお、抽象クラスに抽象プロパティを定義し、具象クラスに属性を要求することができますが、`@abc.abstractproperty`はバージョン 3.3 で非推奨となっているため、このデコレータは冗長になります。

### ファクトリメソッド
ファクトリメソッドはオブジェクト思考のデザインパターンの一つになります。

静的メソッドやクラスメソッドのなどの様に、何か専用にメソッドがある訳ではありません。上記で解説したクラスメソッドや抽象メソッドを使用して、ファクトリメソッドを表現することができます。

ファクトリメソッドの特徴はオブジェクト作成時に、作成するオブジェクトのクラスをサブクラスに選ばせます。

メリットはコードが疎結合になることです。テストが容易になり、プログラム全体に影響を与えたり、壊したりすることなく、特定のコンポーネントに機能を追加できます。デメリットは多くのクラスを作成すると、最終的には読みにくくなります。 

# おわりに
コードの簡潔化及び保守性が高いプログラムの開発を行うためには、メソッドの本質を理解することが重要です。