とらりもんHOME  Index  Search  Changes  Login

とらりもん - python入門: オブジェクトとは Diff

  • Added parts are displayed like this.
  • Deleted parts are displayed like this.

以前, pythonはオブジェクト指向である, と学んだが, ここでその意味を説明する。まず, 以下の例をやってほしい。

pythonシェル(ipython3)で以下を打とう:
z=2+3j
z
z*z
このz=2+3jというのは, 実は複素数2+3iを表している。虚数単位は普通の数学ではiと書くが, pythonではなぜかjと書く約束である! z*zは(2+3i)(2+3i)=4+12i-9=-5+12iであり, 実際, その結果が(-5+12j)と表示されただろう。

では, このzの型は何だろうか? 調べてみよう:
type(z)
すると, complexと出てきた。complexとは, complex number(複素数)の略である。

さて, 複素数には, 実部と虚部がある。この複素数zの実部と虚部をそれぞれ取り出すことができる:
z.real
z.imag
それぞれ2.0と3.0というふうに表示されただろう。(realは実部, imagはimaginaryの略で虚部)

複素数には「複素共役」という概念がある。虚部の符号を逆転させた複素数だ。上の例の複素数zについて, 複素共役を求めるには以下のようにやればよい:
z.conjugate()
すると2-3jと出てくるだろう。conjugateは「共役」を意味する英語である。なぜ()をつけるかは後で説明する。

ここ注意して欲しいのは 君がzを定義したとき, z=2+3jと打っただけで, z.realやz.imagやz.conjugate()というような命令は定義していなかった, にもかかわらず, これらの命令をpythonは最初から知っていたかのように実行したということだ。

これがオブジェクト指向である。どういうことかというと, pythonはあらかじめ「複素数」という概念を知っているのだ。つまり, 複素数は2つの実数から構成されるとか, その片方を実部(real), もう片方を虚部(imag)という, とか, 複素共役(conjugate)とは虚部の符号を逆転させて新たな複素数を作ることだ, とかを知っているのだ。そのような属性(部品的な情報; この場合は実部の値や虚部の値)や操作(この場合は複素共役をとること等)をひっくるめてひとつの「複素数」という概念としてpythonは理解しているのだ。そのような概念(属性と操作のパッケージ)を{{fontc(オブジェクト, red)}}とよび, それを中心に据えて計算機言語を設計することをオブジェクト指向と呼ぶのだ。

ここで考えて欲しいのは, 複素数とは何かということである。複素数の定義は, 2つの実数x, yについてx+yiと表される数である(python的に書くなら, x+yj)。ところが, それは複素数の全てではない。例えば複素数どうしの四則演算をどう定義するか, とか, 複素数の絶対値や偏角, 複素共役, 極型式など, 複素数に付随する様々な概念や定義がある。それらもまとめて複素数という概念の一部としてパッケージ化して計算機に教えておくのである。それが複素数という「オブジェクト」の発想である。

この, {{fontc(オブジェクトの中のデータや操作を使うには、ドット"."をつけてその後に何かを書き足す, red)}}, という書式をする。上の例では, zというオブジェクトに.realをつけてz.realとすることで, 「zの実部」というデータが取り出せたし, .conjugate()をつけてz.conjugate()とすることで, 「zの複素共役を求める」という操作ができた。

このように, pythonでは, {{fontc(ドット"."でつないでどんどん書き足す, red)}}ことで, いろんなことができるようになる。

!!クラスとインスタンス

複素数というオブジェクトは, 丁寧に考えると,
* 複素数という概念(データや操作)の抽象的な定義
* そのように定義された概念にあてはまる具体的なもの
という2つの意味がある。pythonは前者を既に知っているが, 後者はpythonを使う人がその都度, 作るのである。

z=2+3j
と打った時に作られるzというものは後者である。前者は
type(z)
と打った時に現れる"complex"である。"complex"という概念のひとつの具体なものとして, z=2+3jが作られるのだ。

このような考え方において, 前者(概念の定義)を「{{fontc(クラス, red)}}」と呼び, 後者(その概念にあてはまる具体的なもの)を「{{fontc(インスタンス, red)}}」と呼ぶ。
* complexはクラス
* z=2+3jはそのインスタンス
である。

実は, これまで出てきた, intやfloat, str, listなどの型は, クラスなのだ。なぜ最初からクラスと呼ばなかったか? そう呼ぶと諸君が混乱すると思ったからである(実際, 多くのテキストでは, intやfloatはクラスと呼ばれる前に, 型と呼ばれる)。クラスは型を拡張したものだと思ってよい。

いま使っている変数などがどのようなクラスのものなのかを変数のクラスについて詳細を知るのに, help()という命令がある。以下を試してみよう:
x=2+3j
type(x)
help(x)
すると, 以下のような表示が出るだろう(終わるにはqを押す):
Help on complex object:

class complex(object)
  |  complex(real[, imag]) -> complex number
  |  
  |  Create a complex number from a real part and an optional imaginary part.
  |  This is equivalent to (real + imag*1j) where imag defaults to 0.
  |  
  |  Methods defined here:
  |  
  |  __abs__(self, /)
  |      abs(self)
...


!!メソッドとデータ

複素数というオブジェクトは, 丁寧に考えると,
* 実部と虚部というデータ
* 複素共役をとる, 絶対値をとる, などの操作
というふうに, データと操作という2種類の概念で構成される。前者を「{{fontc(データ, red)}}」と呼び(そのままやん!), 後者を「{{fontc(メソッド, red)}}」と呼ぶ。z.realやz.imagはzというオブジェクト(complexクラスのインスタンス)のデータであり, z.conjugate()はzというオブジェクト(complexクラスのインスタンス)のメソッドである。

なお, メソッドやデータは, 次のようにインスタンスに直接つけることもできる(やってみよう!):
(2+3j).real
(2+3j).conjugate()

[レポート課題8-1] 以下のようにxを定義する:
x=[1,2,1,2,3,2,5,4]
* xはどのようなクラスのインスタンスか?
* 以下を打ってみよ。その結果を見て, x.count()というメソッドとx.sort()というメソッドは, それぞれ何をするものか考えよ。
x.count(1)
x.count(2)
x.count(3)
x.sort()
x

[レポート課題8-2] 'I have a pen.'はstrクラスのインスタンスである。以下を打ち, upper(), lower(), replace()というメソッドがそれぞれ何をするものか考えよ。
'I have a pen.'.upper()
'I have a pen.'.lower()
'I have a pen.'.replace('a pen', 'an apple')