banner: ‘I love nature and nature loves me …..’ by CLAUDIA DEA (トリミング) under a Creative Commons Attribution 2.0.
kujira_classというMODを新しく作ります。
└─kujira_class
└─Assets
└─Python
│─KujiraEventManager.py
│
└─Entrypoints
└─CvEventInterface.py
これもリセットしておきます。
from CvPythonExtensions import *
import CvEventManager
import CvUtil
gc = CyGlobalContext()
class Hito:
def getAge(self):
return 18
class MyEventManager(CvEventManager.CvEventManager, object):
def onGameStart(self, argsList):
'Called at the start of the game'
super(self.__class__, self).onGameStart(argsList)
##########
今日は型を新しく自作します。
まずその部分のコードを見てみましょう…
>>>>>>>>>>
class Hito:
def getAge(self):
return 18
<<<<<<<<<<
これはHito型という型の定義です。
実際に使うところも見てみましょう…
>>>>>>>>>>
a = Hito()
age = a.getAge()
<<<<<<<<<<
2つとも代入文ですね。
a = Hito()
でHito型の値を作りa
に代入しています。
age = a.getAge()
では「Hito型の変数a
の」関数であるgetAge()
を呼び出しています。
getAge()
はreturn文で18
を返しますから、age
には18
が代入されます。
変数の中身は直接見ることはできません。
文字列に埋め込んで、表示してみましょう…
from CvPythonExtensions import *
import CvEventManager
import CvUtil
gc = CyGlobalContext()
class Hito:
def getAge(self):
return 18
class MyEventManager(CvEventManager.CvEventManager, object):
def onGameStart(self, argsList):
'Called at the start of the game'
super(self.__class__, self).onGameStart(argsList)
##########
a = Hito()
age = a.getAge()
message = "age=%d" % age
CyInterface().addImmediateMessage(message, "")
今使ったのはクラスという仕組みです。
class クラス名:
とすると、直後のブロックの内容がクラスとして定義されます。
クラス名は同時に型名にもなっていて、あとでクラス名()
とするとその型の値を生成することができます。
クラス型の値ひとつひとつを特にインスタンスと呼びます。
クラス→型、インスタンス→値、と対応しています。ついてきていますか?
クラス定義に戻りまして、ブロックには関数の定義が並びます。
大体普通の関数と同じなのですが、def getAge(self):
の行を見ると、
第1引数がself
になっていることが見て取れます。
このself
の中身が、実はインスタンスになっています。
…………どういうことでしょうか?
少し上で「age = a.getAge()
では『Hito型の変数a
の』関数であるgetAge()
を呼び出しています。」と説明をしました。
意味合い的には「a
の年齢を取得する」になるわけです。
なので、getAge()
を処理するにあたって「自分は誰なのか?」に当たる情報、
自分の持ち主たるインスタンスa
の情報を利用できるように、
self
にa
が代入された状態でgetAge()
が呼び出されるわけです。
(わかりずらいでしょうか…わからなくてもとりあえず読み進めてください)
クラス定義ブロックの中にある関数のことを特にメソッドとよびます。
普通にメソッドを定義するとインスタンスの所有物になり、
そのようなメソッドをインスタンスメソッドと呼びます。
インスタンス.インスタンスメソッド名(追加の引数)
とすることで
インスタンスメソッドを呼び出すことができます。
インスタンスメソッドの第1引数self
はインスタンスだというのは書いた通りですが、
ただのインスタンスではなく「この関数を呼び出した所有者たる」インスタンスなので、
特にレシーバと呼ぶこともあります。
そして、civ4MODのやりたいことのほとんどが
このインスタンスメソッドの仕組みにより行われます。たとえば、
pCity
の人口を4にするpPlayer
の文明IDを取得するpUnit
に昇進e
を持たせるこれらはそれぞれ、
pCity.setPopulation(4)
pPlayer.getCivilizationType()
pUnit.setHasPromotion(e, True)
このようなインスタンスメソッドの呼び出しになるわけです。
Hito型に戻りましょう。
HitoクラスのインスタンスメソッドgetAge()
は、今のことろ
レシーバのself
を無視して、常に18
を返しています。
これではやや味気ないので、インスタンスの情報から年齢を読み取って
返すようにしてみましょう。このようにします。
>>>>>>>>>>
def getAge(self):
return self.age
<<<<<<<<<<
インスタンスの所有するメソッドがインスタンスメソッドだったように、
インスタンスの所有する変数をインスタンス変数と呼びます。
インスタンス.インスタンス変数名
で代入したり参照したりできます。
ここでは「インスタンスself
の変数age
」の値を戻り値として返しています。
self.age
にはまだ値がありません。
そのまま読み出そうとするとエラーになってしまいますから、
代入して設定するためのメソッドを新しく作ります。
>>>>>>>>>>
def setAge(self, age):
self.age = age
<<<<<<<<<<
self
はレシーバです。そのほかに、age
という引数をとって、
それを「self
のage
」に代入します。
a
のsetAge()
を呼び出すにはa.setAge(25)
のようにします。
呼び出すときにはレシーバself
は自動的に設定されますので、
self
以外の引数を指定しましょう。
ここまでをまとめてプログラムしてみましょう…
from CvPythonExtensions import *
import CvEventManager
import CvUtil
gc = CyGlobalContext()
class Hito:
def getAge(self):
return self.age
def setAge(self, age):
self.age = age
class MyEventManager(CvEventManager.CvEventManager, object):
def onGameStart(self, argsList):
'Called at the start of the game'
super(self.__class__, self).onGameStart(argsList)
##########
# aさんをつくる
a = Hito()
# aさんを25歳にする
a.setAge(25)
# aさんの年齢を聞いてageに代入する
age = a.getAge()
message = "age=%d" % age
CyInterface().addImmediateMessage(message, "")
インスタンス変数やインスタンスメソッドは同じ要領でいくつでも作ることができます。
Hito型に名前を持たせてみましょう。
>>>>>>>>>>
def getName(self):
return self.name
def setName(self, name):
self.name = name
<<<<<<<<<<
これで、a.setName("Warrior")
やa.getName()
などとできるようになりました。
もうすこし複雑な例として、名前と年齢から自己紹介っぽい文字列を作るメソッドを見てみましょう。
>>>>>>>>>>
def getSelfIntro(self):
s = "I'm %s, %d years old." % (self.name, self.age)
return s
<<<<<<<<<<
これらを使って…
from CvPythonExtensions import *
import CvEventManager
import CvUtil
gc = CyGlobalContext()
class Hito:
def getAge(self):
return self.age
def setAge(self, age):
self.age = age
def getName(self):
return self.name
def setName(self, name):
self.name = name
def getSelfIntro(self):
s = "I'm %s, %d years old." % (self.name, self.age)
return s
class MyEventManager(CvEventManager.CvEventManager, object):
def onGameStart(self, argsList):
'Called at the start of the game'
super(self.__class__, self).onGameStart(argsList)
##########
# aさんをつくる
a = Hito()
# aさんを25歳にする
a.setAge(25)
# aさんをジョンにする
age = a.setName("John")
# 自己紹介を表示
message = a.getSelfIntro()
CyInterface().addImmediateMessage(message, "")
こんな感じでしょうか。
いまのHito型は、年齢と名前をまず設定しないといけません。
設定しないまま年齢を取得しようとしたり自己紹介を生成しようとすれば
「値がないのに取り出そうとした」のエラーになってしまいます。
どうせ必ず設定しなければいけないのなら、最初にHito型のインスタンスを作るときに
a = Hito("Emily", 35)
みたいにできれば忘れることもなく安心なような気がします。
それを実現するには、__init__()
( 前と後ろにアンダーバー(_)2つずつ )というメソッドを定義します。
>>>>>>>>>>
def __init__(self, name, age):
self.name = name
self.age = age
<<<<<<<<<<
そうすると、a = Hito("Emily", 35)
と書けるようになり、
逆にa = Hito()
とは書けなくなります。
インスタンスを作るときに呼び出されるメソッドをコンストラクタと呼び、
コンストラクタが要求している(self
以外の)引数をすべて与えないと
インスタンスを作ることができなくなります。
この仕組みによって、インスタンスにとって必須なインスタンス変数を
設定し忘れるエラーを未然に防げるため、べんりです。
インスタンスは1つであるとは限りません。
>>>>>>>>>>
a = Hito("John", 25)
b = Hito("Emily", 35)
c = Hito("Montezuma", 7)
<<<<<<<<<<
むしろこのようにどんどん作っていく方が便利に使えます。
同じクラス(型)から複数の異なるインスタンス(値)が作れるので、
都市型のインスタンス、ユニット型のインスタンス、プレイヤー型のインスタンス
などなどに応用が利くのですね。
Hito型のインスタンスも3人ほど作って、それぞれに自己紹介させてみましょう…
from CvPythonExtensions import *
import CvEventManager
import CvUtil
gc = CyGlobalContext()
class Hito:
def __init__(self, name, age):
self.name = name
self.age = age
def getAge(self):
return self.age
def setAge(self, age):
self.age = age
def getName(self):
return self.name
def setName(self, name):
self.name = name
def getSelfIntro(self):
s = "I'm %s, %d years old." % (self.name, self.age)
return s
class MyEventManager(CvEventManager.CvEventManager, object):
def onGameStart(self, argsList):
'Called at the start of the game'
super(self.__class__, self).onGameStart(argsList)
##########
a = Hito("John", 25)
b = Hito("Emily", 35)
c = Hito("Montezuma", 7)
messageA = a.getSelfIntro()
CyInterface().addImmediateMessage(messageA, "")
messageB = b.getSelfIntro()
CyInterface().addImmediateMessage(messageB, "")
messageC = c.getSelfIntro()
CyInterface().addImmediateMessage(messageC, "")