Pythonのお勉強7回目

みんなのPython』を使っての読書会の6回目と7回目。


今回も6回と7回をまとめて一気に。
範囲はそれぞれP.203〜P.254、P.255〜P.307。


日が経って忘れているところも多いので、気になった部分のみをかいつまんでいく。
詳細な報告はこのへんを参照ください。

標準ライブラリ

必要になった時に改めて見るって事でとりあえず斜め読み。


気になったのはcalendarモジュールのmonth関数。
西暦と月を渡すと該当の月のカレンダーを、整形された文字列として返してくれるというもの。
以下が出力イメージ

>>calendar.month(2008,6)
	June 2008\n
Mo Tu We Th Fr Sa Su\n
                   1\n
 2  3  4  5  6  7  8\n
 9 10 11 12 13 14 15\n
16 17 18 19 20 21 22\n
23 24 25 26 27 28 29\n
30\n

改行コードで整形された文字列なんて何に使うんだ??
しかも月、曜日が英語決め打ちだし。。。


普段からターミナルのみで生活してる人がカレンダーを確認するのに便利なのかもしれない。

リスト内包表記

こっからが本番。いよいよPythonならではのコードを書くための文法。
まずはリスト内包表記。

# 1から10までをそれぞれ2乗した数値のリストを取得
>>[ i * i for i in range(1,11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

さらに対象を偶数だけに絞った場合、

>>[ i * i for i in range(1,11) if i % 2 == 0]
[4, 16, 36, 64, 100]

forの左側が要素に適用する式、
for文が集合とその要素、
if文がフィルタリングする要素の条件を表す。


前にfor文やった時にわからなかった内包表記を使ったデカルト積の取り方がわかった。
for i in [1,2,3] で一つの集合( x<-{1,2,3} )を表現しているという事なので、
単純にこれを二つ書けばよい。

>>[[x,y] for x in [1,2,3] for y in [4,5,6]]
[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]


なんかhaskellerlangのそれとは随分書き方が違うけど、
プログラマ的にはこっちの方が直観的なのかな??
JavaScriptで取り込まれた配列内包もこっちの方式だしね。

イテレータ

シーケンスをiter関数を使ってイテレータに変換し、
next関数を呼び出す事で次の要素を取得する。
要素がなくなるとStopIterationというエラーを発生する。

>> i = iter([1,2,3])
>>i.next()
1
>>i.next()
2
>>i.next()
3
>>i.next()
#エラー

エラーを発生させるという事は例外処理を学習しないと使えないか。


for文のシーケンスに使ってみる。

for i in iter([4,5,6]):
	print i

4
5
6

for文ならエラーを発生させずに終わった。
という事は自作のイテレータをfor文に渡せばいいわけだ。
StopIterationを投げた時点でforループを抜けるはず。
。。。が今度はStopIterationの投げ方がまだわからないのでとりあえず保留。

ジェネレータ

通常の関数をreturnの代わりにyieldを使って値を返すと、ジェネレータ関数となる。
ジェネレータ関数を呼び出すとイテレータを生成して返してくれる。
yieldによって返される値はイテレータのnextを使って取得する。

def get_gene():
	print 1
	yield
	print 2
	yield
	print 3

>>i = get_gene()
>>i.next()
1
>>i.next()
2
>>i.next()
3

関数を途中で止めているわけだ。その関数を再開させる事ができるのが呼び出し側。
マルチスレッド的な処理に見えなくもない。

lambda式

好物のlambda式の登場。
一時的に定義するまでもない使い捨て関数として利用できる。

#受け取ったオペレータによってシーケンスを畳み込みする関数
def fold(f,accum, seq):
	for i in seq:
		accum = f(accum, i)
	return accum

>>fold(lambda x,y: x + y, 0, [1,2,3])
6
>>fold(lambda x,y: x * y, 1, [2,2,2])
8

return文無しで書けるのがすっきりしてて良い。
lambda式万歳。

クラス

メソッドの第1引数は必ずインスタンス自身を参照する為の変数が必要。
selfを使うのが慣例?
コンストラクタは__init__をオーバーライドする。

class MyClass:
  def __init__(self):
    print "constructor"
  def method1(self):
    print "hoge"


>>i = MyClass()
"constructor"
>>i.method1()
"hoge"

無意味な仮引数が鬱陶しいがendが必要ないのはいいかもしれない。
インスタンス化にnewキーワードが無いあたりが関数的か。


インスタンス変数に_(アンダースコア)を二つ付けると、
privateなアクセスのみとなるようだ。


クラスの継承もできる。

class Parent:
  a = 100

class Child(Parent):
  b = 200

>>c = Child()
>>c.a
100
>>c.b
200

記述がシンプル。


あるインスタンスの属性をリストとして取得するにはdir()関数を使う。

>>dir(Child())
['__doc__', '__module__', 'a', 'b']


またクラスの特殊メソッドをオーバーライドして定義する事で、
オブジェクト同士の演算を行えるようになる。


例えば__add__メソッドの場合

class MyClass:
  a = 100
  def __add__(self, other):
    return self.a + other.a

>> MyClass() + MyClass()
200

他にも__sub__、__mul__、__eq__等がたくさんある。
しかしpythonは予約後が少ないってのが売りみたいに書いてたけど、
これってほとんど予約後みたいなもんだよね。



そういや継承を行った時にdelを使った場合の挙動が面白かった。

class Parent:
  a = 100

class Child(Parent):
  b = 100

>>c = Child()
>>c.a
100
>>del(Parent.a)
>>c.a
#エラー

親クラスの変数aを削除してやると、既にインスタンス化したc.aも無くなった。
インスタンスとクラスの変数が共有されてるって事かな。

class Parent:
  a = 100

class Child(Parent):
  b = 100

>>c = Child()
>>del(Parent)
>>c.a
100

今度は親クラスそのものを消してみたがちゃんと参照できた。
クラスを消しても内部的な参照は残っているようだ。

class Parent:
  a = 100
class Child(Parent):
  a = 200

>>c = Child()
>>c.a
200
>>del(Child.a)
>>c.a
100

今度は子クラスで親クラスの変数を隠してインスタンス化してから、
子クラスの変数を削除したら親クラスの変数が見えてきた。

class Parent:
  a = 100

>>c1 = Parent()
>>c2 = Parent()
>>c1.a
100
>>c2.a
100
>>c1.a = 200
>>c2.a
100
>>del(c1.a)
>>c1.a
100

二つのインスタンスを作って、一つに値を代入。
後からの代入はインスタンスそのものに代入される。


これらから推測するにクラスとインスタンスはプロトタイプとして繋がっていて、
変数の参照時はクラスを遡って参照し、代入時はインスタンスそのものに代入するJavaScriptのプロトタイプチェーンと同じような事が行われているのがわかる。
という事はpythonはプロトタイプベースなわけか。


ようやくpythonの全貌が見えてきたなぁ。