Pythonプログラミング

【Python】14.クラスとメソッド、オブジェクト

Pythonはオブジェクト指向のプログラミング言語です。
まずオブジェクトが何かと言えば、「データを抽象的に表したもの」だそうです。
Pythonのプログラムが操作したり、処理したりするデータのことを「オブジェクト」と言います。
概念としては「物」です。コード的には「変数と関数をセットにしたもの」です。
電脳空間に入れる人がオブジェクトを見れば、なにか姿かたちのある物に見えるでしょう。

クラスとメソッド

オブジェクトを作る金型のようなものをクラスと呼びます。
クラスを作るには、次のように記述します。
class className():
名前は任意です。この後にクラスの中身を書きます。
中身となる関数がメソッドです。一連の処理に名前をつけたものです。
例として、大名が人材登用を行うクラスを作ってみます。ご先祖様を勝手に使ってしまいますが、木原吉次が登用されるメソッドを書いてみます。

class myClass:
    def hire(self):
        print("人材登用を行う")

    def touyou(self , name):
        print("プレイヤー1は" + name + "を召し抱えた")

player1 = myClass()
player1.hire()
player1.touyou("木原内匠吉次")

1行目でクラスを作る宣言をします。とりあえず何でもいいのでmyClassとします。
メソッドのひとつめ、登用するhireという関数を作ります。引数は「オブジェクト自身」を表すselfを入れます。これについてはまた説明します。
関数の処理は「人材登用を行う」を出力するとしましょう。
2つめの関数touyouを作ります。第一引数は先程のselfでオブジェクト自身に処理を加えることにます。第二引数は何でもいいですが名前という事でnameにします。
引用されたnameをテキストと一緒に出力する事にします。
myClassの型でplayer1というオブジェクトを作ります。オブジェクトplayer1はmyClass内のメソッドの各関数の機能を持っています。
player1にhireを実行せよと命令してみます。
次はplayer1に「touyouを行え。引数は木原内匠吉次だ」と命令してみます。

出力結果
人材登用を行う
プレイヤー1は木原内匠吉次を召し抱えた

ご先祖様は無事に召し抱えられる事ができました!

続いて、武将というクラスを作って複数の武将に命令をしてみましょう。
ここで「init」という謎の人が出てきます。こいつはクラスから実体化する時に必要で、必ず最初に呼び出されるルールになっています。
対象のクラスのインスタンスを初期化するために使うもので、コンストラクタなどと呼ばれます。

class Myclass:
    def __init__(self, busyou):
        self.busyou = busyou
    def naisei(self):
        print(self.busyou + "は内政を行った。石高が3上がった。")

busyou_01 = Myclass("黒田長政")
busyou_01.naisei()

busyou_02 = Myclass("蒲生氏郷")
busyou_02.naisei()

まずクラスを定義する事を宣言し、名前が思いつかないのでMyclassとします。最初は大文字です。
関数を定義します。コンストラクタを入れて、引数はself(インスタンス自身)、変数「busyou」とします。
与えられた値をbusyouと定義します。
メソッド内政を定義します。インスタンスbusyouが行うテキストを入れます。
インスタンス(オブジェクト)busyou_01を定義します。Myclassを元にインスタンス化して引数は黒田長政とします。
インスタンスbusyou_01にnaiseiをさせます。
インスタンスbusyou_02を定義します。Myclassを元にインスタンス化して引数は蒲生氏郷とします。
インスタンスbusyou_02にnaiseiをさせます。

出力結果
黒田長政は内政を行った。石高が3上がった。
蒲生氏郷は内政を行った。石高が3上がった。

Returnしなくても戻り値が返されているのがわかります。
なお、一度作ったインスタンスは、
上にあるように、コンストラクターはクラスからオブジェクトを作るときに最初に呼ばれます。
self.busyouはインスタンス変数と言い、その名の通りインスタンスが持つ変数です。オブジェクトが存在する限り値が存在するので、この後何度でも行動できます。
繰り返しになりますが、selfはインスタンス変数でメソッドから作られたオブジェクト自身がなにかする事を指しています。

クラスをリスト処理する

上の例では武将を個別に入力していましたが、実際にはいちいち記述する事は稀だと思いますので、リストで処理しましょう。
今回のお題は秀吉の第一次九州征伐です。

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

    def raid(self):
        print(self.name + "は九州方面に出兵した")

busyou = []
busyou.append(Busyou("仙石秀久"))
busyou.append(Busyou("長宗我部元親"))
busyou.append(Busyou("十河存保"))

for troopDispatch in busyou:
    troopDispatch.raid()

クラス「武将」を定義します。1文字目は大文字です。
コンストラクタで初期化します。名前を代入するのでnameとしました。
メソッドはself.nameは九州へ出兵するとして出力します。
空リストを作ります。
空リストにappendで1行づつ要素を追加していきます。
ループ開始。busyouの値がある限りtroopDispatchを繰り返します。
内容は変数troopDispatch(リストに格納された武将の名前)がメソッドraidを行います。

出力結果
仙石秀久は九州方面に出兵した
長宗我部元親は九州方面に出兵した
十河存保は九州方面に出兵した

この後釣り野伏せ戦術で撃退されて、仙石は改易されるんですよね・・・。

戻り値を使った四則計算

さて、このままでは薩摩に勝てないので、仙石と十河に長宗我部家から応援で10%増加して、さらにその後豊臣本体から120%に増強された計算をしてみましょう。
戸次川の戦い以前の仙石家の石高が讃岐10万石、十河家が3万石です。

class Naisei:
    kyouka = 1.2
    def __init__(self, kokudaka, zoukyou):
        self.kokudaka = kokudaka
        self.zoukyou = zoukyou

    def total(self):
        return int(self.kokudaka * self.zoukyou * Naisei.kyouka)


sengoku = Naisei(100000, 1.5)
total = sengoku.total()
print("110%に増員され、かつ120%増強された石高は" + str(total) + "石です")

togou = Naisei(30000, 1.5)
total = togou.total()
print("110%に増員され、かつ120%増強された石高は" + str(total) + "石です")

クラス名はNaiseiにします。
これから出る値に1.2掛けすると仮定します。
コンストラクタ。インスタンス自身にkokudakaと名付けます。
メソッドtotal。
整数を扱うのでintを入れます。kokudakaにzoukyouを掛け、さらにkokudakaを掛けた値を戻り値に返します。
Naiseからインスタンス仙石を作ります。引数は10万、第二引数は掛ける値である1.1です。
変数totalを作り、仙石に対しtotalを行った値をtotalに代入します。(わかりにくい事に後から気づいたのですが、変数totalと関数totalが登場していますね。同じ名前でも問題ありません。)
出力します。
Naiseiからtogouを作り、30000石に1.1倍するとします。
totalを行います。
同じくtotalとして出力します。

出力結果
110%に増員され、かつ120%増強された石高は132000石です
110%に増員され、かつ120%増強された石高は39600石です

仙石の10万石、十河の3万石に1.1倍し、さらに1.2倍した値を出力しました。
そういえば、仙石家の10万石は寄騎の十河家3万石込みでしたね・・・。後から思い出しました。

よく使うメソッド

Pythonのメソッドには、あらかじめ規定されているメソッドがあります。
ここでちょっと、ルイス・キャロルの「不思議の国のアリス」を読んでみましょう。
All in the golden afternoon.Full leisurely we glide.For both our oars, with little skill.By little arms are plied.While little hands make vain pretence.Our wanderings to guide.

これを全て大文字にしてみます。

sampleText = "All in the golden afternoon.Full leisurely we glide.For both our oars, with little skill.By little arms are plied.While little hands make vain pretence.Our wanderings to guide."
print(sampleText.upper())

出力結果
ALL IN THE GOLDEN AFTERNOON.FULL LEISURELY WE GLIDE.FOR BOTH OUR OARS, WITH LITTLE SKILL.BY LITTLE ARMS ARE PLIED.WHILE LITTLE HANDS MAKE VAIN PRETENCE.OUR WANDERINGS TO GUIDE.

規定されているメソッドupperで全て大文字になりました!
規定のメソッドはかなり膨大なので一覧は専門のページに任せまして、よく使うであろうテキスト関連とリスト関連を表にまとめました。

replace()文字列を置き換える
find()文字列を探し, あった場合は, 最小のインデックスを返す. なければ-1を返す
rfind()文字列を探し, あった場合は, 最大のインデックスを返す. なければ-1を返す
index()文字列を探し, あった場合は, 最小のインデックスを返す. なければエラーを返す
rindex()文字列を探し, あった場合は, 最大のインデックスを返す. なければエラーを返す
isalnum()英数であればTrue、そうでなければFalseを返す
isalpha()文字がすべて英字であればTrue、そうでなければFalseを返す
isdigit()文字がすべて数字であればTrue、そうでなければFalseを返す
islower()大小文字の区別がある文字がすべて小文字であればTrue、そうでなければFalseを返す
isspace()文字がすべて空白であればTrue、そうでなければFalseを返す
istitle()文字列がタイトルケースであればTrue、そうでなければFalseを返す
capitalize()頭文字だけを大文字にする
swapcase()大文字を小文字に,、小文字を大文字にそれぞれ変換する
title()文字列をタイトルケースに変換
lower()文字をすべて小文字に変換する
upper()文字をすべて大文字に変換する
split()指定した文字をセパレーターに文字列を区切り, リストに変換
rsplit()split()と反対に右からセパレートしてリストに変換
splitlines()改行でセパレートしてリストに変換
insert()第一引数で指定したインデックスに第二引数の値を追加
append()リストに追加
remove()リストから削除

例えばテキストが全て小文字かどうかを識別するとすると

sampleText = "All in the golden afternoon.Full leisurely we glide.For both our oars, with little skill.By little arms are plied.While little hands make vain pretence.Our wanderings to guide."
print(sampleText.islower())

出力結果
False

となります。
もしTrue/False以外の文字列にしたいとすれば、やりたければif文で分岐してもよいです。

sampleText = "All in the golden afternoon.Full leisurely we glide.For both our oars, with little skill.By little arms are plied.While little hands make vain pretence.Our wanderings to guide."
#sampleText = input()
if sampleText.islower():
    print ("全て小文字です")
else:
    print ("全て小文字ではないです")

あるいは、標準入力するなら次のようになります。

sampleText = input()
if sampleText.islower():
    print ("全て小文字です")
else:
    print ("全て小文字ではないです")

プライベートメソッド、プライベート変数

そのクラスの中でしか呼び出せないメソッドをプライベートメソッドと言います。
メソッド名の前にアンダーバーを2つつける事で指定できます。
同じくクラスの外で呼び出せない変数がプライベート変数です。
試しに、木原吉次を石高300石と設定し、名前を重次(孫)に変更して、石高を400石に加増しようとしてみます。
この際、石高をプライベートメソッドとして外部からの変更不可にしてみます。

class MuClass():
    def __init__(self, name ,no):
         self.myname = name
         self.__private = no 
    def kokudaka(self):
         print(self.myname)
         print(self.__private)

busyou_1 = MuClass("木原吉次", "300石")
busyou_1.kokudaka()
busyou_1.myname = "木原重次"
busyou_1.kokudaka()
busyou_1.__m__private = "400石"
busyou_1.kokudaka()

プライベートメソッドの名前は何でもいいので、とりあえずprivateとして、プライベートメソッドを表す__をつけてみます。
これで__privateを参照するnoは変更できません。

出力結果
木原吉次
300石
木原重次
300石
木原重次
300石

実際に呼び出してみると、名前部分は後から変更できていますが、石高部分は変更できません。
ご先祖様ごめんなさい。

クラスの継承

クラスは他のクラスに使い回す事ができます。これを継承(Inheritance)と呼び、クラスのメソッドを再利用したり、後から追加する事ができます。
継承の仕方は非常に簡単で、
class 新クラス名(継承したい旧クラス名):
これだけで継承できます。
さて、先程は加増をもらいそこねたので、今度は加増されるコードを書いてみます。

class Kokudaka:
    def __init__(self, kome):
        self.kome = kome

    def ryouchi(self):
        print("吉次は" + self.kome + "の領地をもらった。")

class Housyou(Kokudaka):
    def kazou (self):
        print("報奨として加増を受けた。")

koku = Kokudaka("300石")
koku.ryouchi()

Housyou = Housyou("100石")
Housyou.kazou()
Housyou.ryouchi()

石高というクラスを定義します。
コンストラクタで引数を自分・コメとします。
メソッド領地を定義します。
内容はコメ分の領地をもらったとします。

石高を継承するクラス報奨を作ります。
メソッドは加増なので報奨を受けたとします。
インスタンスkokuを作成。引数として300石とします。
その時の石高を出力します。
インスタンス報奨を作成。引数を100石とします。
報奨に対し加増を実行
報奨に対し領地を実行します。
メソッドはそれぞれ出力なので出力結果が表示されます。

出力結果
吉次は300石の領地をもらった。
報奨として加増を受けた。
吉次は100石の領地をもらった。

無事100石加増されましたね。
これでクラスの継承ができました!

コメントを残す

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