Python

【Python】17.クラスメソッド

ちょっと時間が開いてしまったので、まずはおさらいします。
小西家、仙石家で小田原を攻めましょう。
彼らは武士ですが織田信雄は正二位内大臣なので大将として交渉します。


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

    def kougeki(self, busyou):
        print(self.name + "は、" + busyou + "に突入した!")

class Taisyou(Bushi):
    def kougeki(self, busyou):
        self.__spell()
        print(self.name + "は、" + busyou + "へ交渉を行った")
    def __spell(self):
        print("--クラスに入れたテキストを読み出す--")

print("【ここから開始】")

konichi = Bushi("小西")
sengoku = Bushi("仙石")
Taisyou = Taisyou("織田信雄")

toyotomike = [konichi, sengoku, Taisyou]
for seigun in toyotomike:
    seigun.kougeki("北条家")
  1. Class「武士」を定義
  2. nameの値を引き取ってnameに入れます
  3. 関数「攻撃」を定義します。このClassのインスタンスは敵武将に突入します
  4. ここでは継承を使い、武士を継承したClass「大将」を定義します
  5. 大将での攻撃を定義しますが、大将なので力攻めせず交渉を行います
  6. ここで復習として、メソッドに入れたテキストを読み出してみましょう。
  7. 開始地点はここです
  8. 武士からインスタンス小西を作成
  9. 武士からインスタンス仙石を作成
  10. 大将からインスタンス織田信雄を作成
  11. 豊臣武将を一つのリストにします
  12. for文開始。変数名は西軍でリストの値がある限り攻撃を行います。nameは北条家とします。

出力結果:
【ここから開始】
小西は、北条家に突入した!
仙石は、北条家に突入した!
–クラスに入れたテキストを読み出す–
織田信雄は、北条家へ交渉を行った

どこで入れた関数がどこで読み出されるか確認しましょう。

おさらい

ちょっとコンストラクタについておさらいしましょう。
オブジェクト指向言語で、オブジェクトを生成する際に呼び出されて内容の初期化などを行なう関数です。
オブジェクト指向言語では、最初にClass(設計図)を定義しますが、このClassだけでは動作しません。
この設計書をもとにプログラム内で実際に使うためには、実体化(インスタンス化)が必要となります。この時にコンストラクタは勝手に必ず最初に呼び出され、オブジェクトを生成してデータの初期化を行う関数です。

このコンストラクタはアンダーバー2つで囲われます。
ちょっと紛らわしいものに、アンダーバー2つを先頭につけたプライベートメソッドという書き方があります。
プライベートメソッドは基本的にはそのclass以外から呼ぶことはできません。
ただし完全に呼ぶ手段が絶たれたわけではなく、マンダリングという処理が行われているので、呼べないわけではありません。意味がわからないですね。

逆(と言っていいのかわかりませんが)
super()とつけられたメソッドは、classを継承したクラスが、継承元のメソッドを呼び出す事ができます。
子classが親classのメソッドを呼び出すと言ってもいいかも知れません。

大将が行う行動は大将にしか見えないようマンダリングしてみます。


class Bushi:
    bannte = 0
    def __init__(self, name):
        self.name = name
        Bushi.bannte += 1
        print(str(Bushi.bannte) + "番手、" + self.name + "が出陣した。")

    def kougeki(self, busyou):
        print(self.name + "は、" + busyou + "に突入した!")

class Taisyou(Bushi):
    def __init__(self):
        super().__init__("織田")

    def kougeki(self, busyou):
        self.__cyouryaku()
        print(self.name + "は、" + busyou + "を調略した!")

    def __cyouryaku(self):
        print("成田家は寝返った!")

print("合戦開始")
konishi = Bushi("小西")
sengoku = Bushi("仙石")
oda = Taisyou()

toyotomike = [konishi, sengoku, oda]
for seigun in toyotomike:
    seigun.kougeki("北条家")
  1. class武士を定義
  2. 何番手かを表す変数を設定。初期設定は0
  3. 関数を定義、コンストラクタ
  4. 名前を取り出してnameに格納
  5. 読まれたら武士が何番手かの変数に+1するとします
  6. 何番手の武士のどの名前が出陣したかをプリントします
  7. 関数攻撃を定義します。引数は武将
  8. 何という名前の武士が変数「武将」に突入したとプリントします
  9. class大将を定義 武士をオーバーライド
  10. 関数の定義、コンストラクタ
  11. superなので親classのメソッドを引き継げます。名前は織田とします。これで
  12. 田は親classのメソッドに適用されます
  13. 関数「攻撃」を定義。引数は武将
  14. プライベートメソッド「調略」を定義します。調略はマンダリングされ大将しか使えません
  15. name(上で織田という文字を引き取っている)は武将を調略したとプリントします
  16. 関数「調略」を定義。これはプライベート関数なので大将内でしか使えません
  17. 合戦開始とプリント
  18. 武士からインスタンス小西を作成
  19. 武士からインスタンス仙石を作成
  20. 大将からインスタンスを作成します。スーパーメソッドなので武士で作られたインスタンスに織田とつけて作成されるはずです
  21. リスト豊臣家に小西、仙石、織田を格納します
  22. for文開始。変数は西軍とし、豊臣家のリストがある限り変数西軍はメソッド「攻撃」を繰り返します。引数は文字列「北条家」とします

出力結果:
合戦開始
1番手、小西が出陣した。
2番手、仙石が出陣した。
3番手、織田が出陣した。
小西は、北条家に突入した!
仙石は、北条家に突入した!
成田家は寝返った!
織田は、北条家を調略した!

まず最初の処理で「合戦開始」が表示されます。
インスタンスはコンストラクタinitが最初に呼ばれるので、インスタンス化するとまずBushi.bannteに+1して「出陣した」という表示になります。
小西、仙石は武士なのでそれぞれ出陣します。
織田は武士を継承した大将なのでちょっと違い、スーパーメソッドなのでclass外で呼ぶ事ができ、後から織田と引数がつけられていますが、舞い戻って武士init内の処理で3番手で出陣します。子classから取り出した引数を親classのinitで使用している事がわかります。
さてループは攻撃なので、それぞれkougekiを行います。
小西はbusyouに対してkougekiに定義された突入します。busyouは引数の「北条家」が使用されます。
仙石も同様です。
織田の攻撃は武士で定義されていますが、class「大将」の中で「調略」に再定義されています。

次の関数で定義された調略を行いますので、「成田家は寝返った!」が表示されます。
次にこのインスタンスはbusyouを調略したと表示されますので、「織田は、北条家を調略した!」という表示になります。
「成田家は寝返った!」と「織田は、北条家を調略した!」が逆の順番でも処理できるわけですね。
織田はsuperがついているので、class武士のメソッドを実行し、classの外で呼び出せています。
また、superは親classから継承するだけでなく新たに調略、寝返らせるという機能が追加された事がわかります。

クラスメソッド

classに付随しているメソッドのようなものをクラスメソッドと呼びます。
メソッドはインスタンス化しないと呼べませんでしたが、クラスメソッドはインタンス化されていないclassからも呼び出せます。
クラスメソッドにするにはメソッドに @classmethodと記述します。
今までのメソッド(インスタンスのメソッド。メソッドは英語で書くとMethod)の第一引数はself(そのインスタンス)でしたが、class自体になります。
classMethodは関数は似ていますが、
・第一引数がclass
・classの中にあればインポートすれば呼べる
という違いがあります。
この@classmethodや@staticmethodのように関数の中身を変えずに関数をデコレーションする命令をデコレータと呼びます。
classMethodの引数はclassの意味で(cls)と書きます。

では、先程のコードの一部にclassMethodを追加して試してみましょう。


class Bushi:
    __bannte = 0

    @classmethod
    def heiryoku(cls):
        print(str(Bushi.__bannte) + "の兵力で出陣した。")

    def __init__(self, name):
        self.name = name
        Bushi.__bannte += 1
        print(str(Bushi.__bannte) + "番手、" + self.name + "が出陣した。")

    def kougeki(self, busyou):
        print(self.name + "は、" + busyou + "に突入した!")

class Taisyou(Bushi):
    def __init__(self):
        super().__init__("織田")

    def kougeki(self, busyou):
        self.__cyouryaku()
        print(self.name + "は、" + busyou + "を調略した!")

    def __cyouryaku(self):
        print("成田家は寝返った!")

print("合戦開始")
konishi = Bushi("小西")
sengoku = Bushi("仙石")
oda = Taisyou()

toyotomike = [konishi, sengoku, oda]
for seigun in toyotomike:
    seigun.kougeki("北条家")

Bushi.heiryoku()
  1. class武士を定義
  2. プライベート変数「番手」を定義。初期値0
  3. デコレータでclassMethodを宣言
  4. 関数「兵力」を定義
  5. クラスメソッド武士+番手の形で出陣したとプリントしようとしてみます
  6. コンストラクタ
  7. 名前を引き取ってnameに格納します
  8. 武士+番手を一度実行されたら+1するとします
  9. 武士+番手のname(引き取られた名前)が出陣するとプリントします
  10. 関数「攻撃」を定義します
  11. nameは武将に突入したとプリントします
  12. class大将を定義し、武士をオーバーライドします
  13. コンストラクタ
  14. これをスーパーメソッドにします名前は織田とします
  15. 関数「攻撃」を定義します引数は武将にします
  16. nameは武将を調略したとプリントします
  17. プライベート関数調略を定義します
  18. 成田家が裏切ったとプリントします
  19. プリント「合戦開始」
  20. class武士からkonishiを作成し、名前となる「小西」を格納します
  21. class武士からsengokuを作成し、名前となる「仙石」を格納します
  22. class大将からodaを作成します。これは上の関数で名前を織田としています
  23. リスト豊臣家を作成。小西仙石織田を格納します
  24. ループ開始。変数名は西軍とし豊臣家のリストがある限り繰り返します
  25. ループの内容は西軍が関数「攻撃」を行い引数は文字列「北条家」とします
  26. ここで関係ない場所でクラスメソッド武士+兵力を呼んでみます。

実行結果
合戦開始
1番手、小西が出陣した。
2番手、仙石が出陣した。
3番手、織田が出陣した。
小西は、北条家に突入した!
仙石は、北条家に突入した!
成田家は寝返った!
織田は、北条家を調略した!
3の兵力で出陣した。

基本的に前のコードと同じですが、最後に、どのインスタンスでもないですがclassMethodのBushi.heiryoku()を実行してみます。
class「武士」の「兵力」はデコレータでclassMethodになっていますので、インスタンスに関わらず呼び出す事ができます。
Bushi.__bannteの兵力で出陣したと表示するわけですが、関数「兵力」内でカウントは加算しないので、先程の3のままとなります。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です