November 03, 2003

初めての Python - Iterator パターン

[ Python ]

あれから随分経ってしまったけど、LL Saturday の講演を聴いて Python が面白そうだったので、品川の書店に出向き Python 本を買ってきました。本当はネズミ本こと『初めてのPython』が欲しかったのですが、3件ほど回っても見当たらなかったので、表題が僕にちょうど良さそうな『PerlユーザのためのPython移行ガイド』を購入しました。

PerlユーザのためのPython移行ガイド

その辺のスタバで半分ちょっと読んで帰宅。この本、Perl ユーザのための...ということで、無駄な部分が少なくて効率的に学習できたんですが、肝心のオブジェクト指向プログラミングについての記述がちょっと足りないなあ。5ページ分しか載ってない...。ちょっと選択誤ったかな。

とりあえず、何か書いてみないことには始まらないということで、早速 Python の安定版 2.3.2 を Linux ボックスにインストール。Emacs の Python Mode やスクリプト内で UTF-8 以外の日本語を扱うための追加モジュールも入れました。

さて、何か題材を探そうということで結城浩さんの名著『Java言語で学ぶデザインパターン入門』に載っているデザインパターンを Python で実装してみることにしました。言語に依存しないデザインパターンなら覚える文法も必要最低限で済むということで。

Java言語で学ぶデザインパターン入門

まずは、Iterator パターンです。

集合体を表すクラスのインタフェース Aggregate - aggregate.py

class Aggregate:
    def iterator(self):
        pass

Perl に同じく、Python もメソッド呼び出しは動的なので、Java の interface のような強制力を、実装クラスに持たせることはできないみたいです。ので、aggregate.py は単なる気休めでしかないのかも。ただし、プログラマの意図を汲み取るという上で実装しておくのは一利あると思います。こんな実装でいいかどうかは別として。(笑)

例によってインデントが分の区切りです。最初はなんかとっつきにくいですが、Python Mode がその辺は勝手に制御してくれて、慣れるとちょっと病みつきになってきます。コードが綺麗に書けますね。

イテレータのインタフェース Iterator - iterator.py

class Iterator:
    def hasNext(self):
        pass
 
    def next(self):
        pass

こちらも同じく気休め。Python のメソッドは必ず引数を宣言しておかないといけないようです。pass は何もしないの意味。hasNext() も next() もインタフェースなので pass のみ。例外を投げる方がインタフェースらしくていいかな?

Aggregate の実装 BookShelf - bookshelf.py

from aggregate import Aggregate
from bookshelfiterator import BookShelfIterator
 
class BookShelf (Aggregate):
    def __init__(self):
        self.books = []
        self.last = 0
        pass
 
    def iterator (self):
        return BookShelfIterator(self)
        
    def bookAt (self, i):
        return self.books[i]
 
    def append (self, book):
        self.books.append(book)
        self.last += 1
        pass
 
    def length (self):
        return self.last

Pythonって「++」が使えないのかな。self.last++ とかやったら Syntax エラーと言われました。あと、Perl に慣れていたせいか外部ファイルのインポートの仕方にちょっと戸惑いました。とりあえず上記のような書き方で落ち着きました。aggregate.py から Aggregate クラスをインポート、といった意図です。コンストラクタは __init__ で。配列もオブジェクトなので、要素の追加は self.books.append(book) ですね。

Iterator の実装 BookShelfIterator - bookshelfiterator.py

from iterator import Iterator
 
class BookShelfIterator(Iterator):
    def __init__(self, bookShelf):
        self.bookShelf = bookShelf
        self.index = 0
        pass
 
    def hasNext(self):
        if (self.bookShelf.length() > self.index):
            return True
        else:
            return False
 
    def next(self):
        book = self.bookShelf.bookAt(self.index)
        self.index += 1
        return book

boolean 型の True/False といった組み込み変数が使えますね。

集合体につっこむモノ Book - book.py

class Book:
    def __init__(self, name):
        self.name = name
        pass
 
    def getName(self):
        return self.name
 
    def setName(self, name):
        self.name = name
        pass

Python らしいアクセサの書き方ってどんなのなんでしょうか。Ruby の attr_accessor みたいなのはあるのかな? とりあえずわからなかったので素直に getter/setter を実装。

実行スクリプト - main.py

#!/usr/local/bin/python
# -*- coding: euc-jp -*-
from bookshelf import BookShelf
from book import Book
 
bookShelf = BookShelf()
 
bookShelf.append(Book("PerlユーザのためのPython移行ガイド"))
bookShelf.append(Book("初めてのPython"))
bookShelf.append(Book("Java言語で学ぶデザインパターン入門"))
bookShelf.append(Book("Pythonで学ぶプログラム作法"))
 
iter = bookShelf.iterator()
 
while iter.hasNext():
    book = iter.next()
    print book.getName()

EUC-JP をスクリプト内で使うには、2行目の記述が必要です。ないと怒られます。メソッド呼び出しはたとえ引数がなくても明示的に () を指定しないといけないようです。iter.next とかしちゃうとインスタンスフィールドを操作してることになっちゃうのかな。エラーにすらならないのではまりやすい。

実行結果

[naoya@mary Iterator]$ python main.py
PerlユーザのためのPython移行ガイド
初めてのPython
Java言語で学ぶデザインパターン入門
Pythonで学ぶプログラム作法

おおー、ちゃんと Iterator になっとる。(当たり前田) やっぱり、言語仕様で OO をサポートしているだけあって Perl で書くよりもだいぶスリムなコードになります。ちょっと外部ファイルのインポートのところが慣れないせいか気持ち悪いのだけど。文法自体は、Perl と Ruby の中間みたいな印象を受けるのですが、単にそれは僕が Perl → Ruby(挫折) → Python と来ているからだという噂も。

次は TemplateMethod パターンです。

Posted by naoya at November 3, 2003 02:59 AM | トラックバック (0)  b_entry.gif
トラックバック [0件]
TrackBack URL: http://mt.bloghackers.net/mt/suck-tbspams.cgi/596
コメント [0件]