ホーム > Python > 初めてのPython(6) Pythonでオブジェクト指向プログラミングを

初めてのPython(6) Pythonでオブジェクト指向プログラミングを

初めてのPython“も残り1部です。
ここまで長かった…ですが、とても勉強になりました。
Pythonを学び、”PHPには何が足りないのか“を自分の中で消化出来たような気がします。
それではラストスパートに入って行きましょう。

初めてのPython VI部

初めてのPython“のVI部には”Pythonにおけるオブジェクト指向プログラミング“がまとめられています。
クラスメソッドの構文はもちろんのこと(これがないとクラスを書けませんしね…)、オブジェクト指向の考え方まで。

has-a関係やデリゲーション、ファクトリといった”オブジェクト指向プログラミングの基本的な設計“までもまとめてあります。
このあたりの話はPythonに限ったことではないので、”オブジェクト指向プログラミング“の経験がある方は読み飛ばせると思います。
ただ、単純な構文だけでなく、このような根本の考え方を含めて学べるのは”初めてのPython“の良いところかな、と。

これまでの内容を理解しているのであれば、VI部の内容を理解するのは容易いでしょう。
構文だけを学び、ソースコードを眺めるだけでも良さそうです。

ここでは”オブジェクト指向プログラミング“の経験があることを前提に、Pythonでのオブジェクト指向プログラミングをまとめていきます。

クラスとインスタンス

Pythonではクラスも”オブジェクト“です。
そしてインスタンスも”オブジェクト“です。

全てオブジェクトで扱う。それがPythonです。

そして、クラス宣言も”オブジェクト“を生成し、それを”クラス名の変数“に入れる操作にしか過ぎません。

class Spam:
	def printMe(self):
		print self

SpamCopy = Spam
spamIns = SpamCopy()
spamIns.printMe()
# spamInsがSpamのインスタンスであることが表示される。
# <__main__.Spam instance at 0x7f779b318518>

このように書くことで、クラスを別の変数に入れることも出来ます。

また、インスタンスの生成も変わっていますよね。
関数の呼び出しと同じ宣言の仕方でインスタンスが出来てしまいます。

メソッドも関数と同じ”def“を使います。
ただし、第一引数に”self“を使う点が異なります。
先ほどの例で分かったと思いますが、この”self“には呼び出した自分自身(インスタンス)が入ります。

PHPやJavaで言うところの”this“ですね。
Pythonではこれを明示的にメソッドに示します。
これが結構分かりやすい。

class Spam:
	num = 0
	def add(self):
		self.num += 1
	def getNum(self):
		return self.num

spamIns = Spam()
spamIns.add()
print spamIns.getNum()
# 1が表示される
spamIns.add()
print spamIns.getNum()
# 2が表示される

self“が何者かだけ分かっていれば、扱いやすいと思います。
クラスオブジェクト自身が持つメソッドを使うことだって出来ますよ。

class Spam:
	num = 0
	def add(self):
		self.num += 1
	def getNum(self):
		return self.num

spamIns = Spam()
Spam.add(spamIns)
print Spam.getNum(spamIns)
# 1が表示される
Spam.add(spamIns)
print spamIns.getNum(spamIns)
# 2が表示される

つまり、インスタンスのメソッドを呼び出すときは、Pythonが自動でクラスオブジェクトのメソッドの第一引数を埋めてくれているんです
内部的に何か訳の分からないことをやられるよりは、こっちのほうがずっと分かりやすいです。
クラスとインスタンスの関係を知っていれば良いんですから。

クラスって何?“と聞かれれば、”それは単なる名前空間にしか過ぎない。“と答えることが出来そうです。
class宣言の後には、”名前空間“が出来上がります。
その中に”変数( = 属性)“や”関数( = メソッド)“を宣言していると考えれば良いんです。
だからこそ、”def add(self):“や”num = 0“といった今までと同じ書き方が出来る。

話は戻りますが、クラス宣言も単なる”オブジェクトの生成“と”そのオブジェクトへのリファレンスを持った変数の生成“にしか過ぎません。
そのため、こんなことも出来ます。

class Spam:
	num = 0
	def add(self):
		self.num =+ 1

class Spam:
	num = 0
	def sub(self):
		self.num =- 1
	def getNum(self):
		return self.num

spamIns = Spam()

spamIns.sub()
print spamIns.getNum()
# -1が表示される

spamIns.add()
# エラーが発生する

PHPだと”クラスの二重宣言だ!“って怒られてしまいますよね。
この柔軟性がPythonの素敵なところです。

他にPythonのクラスの特徴として、属性が”ディクショナリ“で管理されていることが上げられます。

class Spam:
	def attributes(self):
		self.egg = 'egg'
		return self.__dict__

spam1 = Spam()
print spam1.attributes()
# {'egg': 'egg'}が表示される。

演算子のオーバーロード

Pythonのクラス宣言で嬉しいのが、この”演算子のオーバーロード“です。
これは何か?“というとインスタンスに対する演算子の処理を独自に定義出来ます
つまり、プラス(+)やマイナス(-)がインスタンスに対して実行出来るんです。

class Company:
	workers = 0
	def __add__(self, other):
		newCompany = Company()
		newCompany.workers = self.workers + other.workers
		return newCompany

	def __sub__(self, other):
		newCompany = Company()
		newCompany.workers = self.workers - other.workers
		return newCompany

company1 = Company()
company1.workers = 10
company2 = Company()
company2.workers = 20

company3 = company1 + company2
print company3.workers
# 30が表示される

company4 = compony2 - company1
print company4.workers
# 10が表示される

ただし、同じクラスのインスタンス同士の演算でない場合、対象のインスタンスが演算子の左右どちらかに書かれるかで動作が変わってきます。
自分自身、あまり理解していないのと、まずは基本形だけ覚えれば良いと思うので、ここではこれだけにしておきます。

Pythonでは”コンストラクタ“も演算子のオーバーロードの一つです。
インスタンスを生成するときに使用する”引数指定“をオーバーロードするイメージでしょうか。

class Company:
	def __init__(self, workers = 0):
		self.workers = workers

company1 = Company()
print company1.workers
# 0が表示される

company1 = Company(20)
print company1.workers
# 20が表示される

演算子のオーバーロードを使えば、クラスに”指定された属性“がない場合の処理も簡単に書けます。

class Company:
	def __getattr__(self, name):
		if name == 'name':
			return '1-byte.jp'
		else:
			# 属性がない場合の例外を発生させる
			raise AttributeError

company1 = Company()
print company1.name
# 1-byte.jpが表示される
print company1.type
# エラーが発生する

その他に、”リスト表記“にも対応することが出来ます。

class Spam:
	def __getitem__(self, num):
		return num * 2

spam1 = Spam()
print spam1[1]
# 2が表示される
print spam1[11]
# 22が表示される

しかし、便利ですね。
色々活用のアイディアが広がります。

継承と多重継承

Pythonではもちろん、”多重継承“も出来ます。
(PHPでは出来ないんですがね…)

class Person:
  name = None
  sex = None

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

  def getName(self):
    return self.name

  def getSex(self):
    return self.sex

class Printer:
  def __str__(self):
    return str(self.__dict__)

class Worker(Person, Printer):
  job = None

  def __init__(self, name, sex, job):
    Person.__init__(self, name, sex)
    self.job = job

worker1 = Worker('John', 'male', 'engineer')

print worker1
# {'job': 'engineer', 'name': 'John', 'sex': 'male'}が表示される
print worker1.getName()
# 'John'が表示される
print worker1.getSex()
# 'male'が表示される

しっかり継承されていることがわかります。
と、ここで気づいたのですが、(多重継承でない)継承の説明をしていませんでしたね。

class クラス名(継承するクラス名):

このステートメントを書くことで、クラスを継承出来ます。
先ほどのコードで分かったと思いますが、多重継承の場合は、

class クラス名(継承するクラス名1, 継承するクラス名2, 継承するクラス名3, …):

と書きます。
属性“や”メソッド“の検索順は書いた順番です。つまり、左側に書いたクラスから順に検索されます

class Spam1:
	name = 'Spam1'

class Spam2:
	name = 'Spam2'

class Spam3(Spam1, Spam2): pass

class Spam4(Spam2, Spam1): pass

spam = Spam3()
print spam.name
# 'Spam1'と表示される

spam = Spam4()
print spam.name
# 'Spam2'と表示される

これだけ理解していれば基本は大丈夫です。

ちょっと複雑な話をすると、継承する形によって検索順が問題になることがあります。
いわゆる”ダイアモンド継承“と呼ばれる形です。

多重継承したクラスを先に検索するのか?それとも、親クラスを先に検索するのか?

このあたりの話は面倒なので、ここには書きません。
知りたいという方は、SIBUKAWAさんの以下の記事が参考になります。

長くなったので、”Pythonのオブジェクト指向プログラミング”の話はここで終わりにしましょう。
本当は”どうやってスタティックメソッドを作るのか“、”情報を隠蔽するのか“など、細かい話は尽きません。

私自身、”初めてのPython“を読みましたが、全てを理解し、活用出来るようになった訳ではありません。
このあたりになってくると、テクニック的な話が多くなりますね。
その辺は自分でアプリケーションを作りながら、ソースコードを読みながら勉強しましょう。

次で”初めてのPython“シリーズは最後です。
ブログ書きながら読んだら2週間かかっちゃいましたね…。

初めてのPython 第3版 初めてのPython

  • 著者: Mark Lutz (著), 夏目 大 (翻訳)
  • 出版社: オライリージャパン
  • 発売日: 2009/02/26

こちらもあわせてどうぞ

ちょっと一言

PHP Matsuriに来ているのにPythonの記事を上げますw
ハッカソンはこれから!多分w

blog comments powered by Disqus

ホーム > Python > 初めてのPython(6) Pythonでオブジェクト指向プログラミングを

スポンサードリンク
書いている人
つぶやき
RSS 気になるニュース
過去の記事

ページの上部に戻る