2009年10月30日金曜日

Google App Engine (GAE)

私が、Pythonの勉強を始めた理由は、GoogleAppEngineを使いたかったからだ。

最近では、Javaのサポートも加わっているが、Googleが初期段階からPythonに対応しているということには、それなりの理由があると考え、Pythonの勉強を始めた。

GoogleAppEngineを実際に使えるようになるまでに、1点はまったので、その点を含めて利用開始までの手順を解説する。

1. アカウントを取得する


http://code.google.com/intl/ja/appengine/にサインインする。G-mailのアカウントでよい。

「AppEngineのアカウントにログインする」をクリック。

もう一度ログイン認証画面がでる。

すると、

Welcome to Google App Engine

Before getting started, you want to learn more about developing and deploying applications.
Learn more about Google App Engine by reading the Getting Started Guide, the FAQ, or the Developer's Guide.

[Create Application]

と表示される。

[Create Application]ボタンをクリックすると、

Verify Your Account by SMS

To create applications with Google App Engine, you need a verification code. Select the country and carrier for your mobile phone and enter your mobile phone number. The verification code will be sent to it via SMS. Note you will only need to verify your account once.

と表示される。

ここの、Country and Carrier: (国とキャリアの選択)で、日本を選んでハマりました。

iPhoneしか持っておらず、メールアドレスの設定もしてなかったので、日本を選ぶとこの先に勧めません。

日本の携帯電話のE-mailアドレスを持っていない場合は、Otherを選び、下に表示されるフィールドに携帯電話の番号を入れましょう。

こうすれば、SMSでパスワードが送信されてきます。

受け取ったパスワードで認証を通せば、アプリケーションを作れるようになります。


2. ツールをインストールする

GoogleAppEngine_1.2.7.msi

インストールし、Google App Engine Launcherを起動。

新しいアプリケーションを作成。
File ⇒ Create New Application

これだけで、ひな形が一式生成されます。

yamlファイル(デプロイに関する記述)
application: study-python
version: 1
runtime: python
api_version: 1

handlers:
- url: .*
script: main.py

以下のアドレスから、私が試しに作成・デプロイしたアプリケーションを確認できます。

http://study-python.appspot.com/

シンプルな数当てゲームです。
O'REILLY著 の Using Google App Engineに出ているサンプルを日本語化し、毎回数字が変わるように改良しています。

2009年10月28日水曜日

HTTPRequest

HTTPRequestを送信してみた。

Googleのトップページを取得

>>> import urllib2
>>> url = "http://www.google.co.jp"
>>> result = urllib2.urlopen(url)
>>> lines = result.readlines()
>>> lines[0]
'<html><head><meta http-equiv="content-type" content="text/html; charset=Shift_JIS"><title>Google</title><script>window.google={kEI:"tirjSpiUK4egwgPJttycDQ",kEXPI:"17259,21766,22107,22217",kCSIE:"17259,21766,22107,22217",kCSI:{e:"17259,21766,22107,22217",ei:"tirjSpiUK4egwgPJttycDQ"},kHL:"ja"};\n'
>>> lines[1]
'\n'
>>> lines[2]
'window.google.sn="webhp";window.google.timers={load:{t:{start:(new Date).getTim
e()}}};try{}catch(b){}window.google.jsrt_kill=1;\n'


このブログからURLを抽出してみた。

data = urllib.urlopen('http://python25.blogspot.com').read()
for url in re.findall(r"https?://[-_.!~*()a-zA-Z0-9/?:@&=+$,%#]+", data):
  print url

>>> for url in re.findall(r"https?://[-_.!~*'()a-zA-Z0-9/?:@&=+$,%#]+", data):
...   print url
...
http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd
http://www.w3.org/1999/xhtml
http://www.google.com/2005/gml/b
http://www.google.com/2005/gml/data
http://www.google.com/2005/gml/expr
http://www.blogger.com/favicon.ico
http://python25.blogspot.com/
http://python25.blogspot.com/feeds/posts/default
http://python25.blogspot.com/feeds/posts/default?alt=rss
http://www.blogger.com/feeds/6658540373074530849/posts/default
http://www.blogger.com/rsd.g?blogID=6658540373074530849
http://www.blogger.com/profile/17765477399828427893
http://www.blogger.com/openid-server.g
http://www.blogger.com/static/v1/widgets/1550194411-widget_css_bundle.css
http://www.blogger.com/static/v1/v-css/3727950723-blog_controls.css
http://www.blogger.com/dyn-css/authorization.css?targetBlogID=6658540373074530849&zx=885dfb85-549b-4231-a3a3-178f9be1dbeb
http://www1.blogblog.com/dots/bg_dots.gif
http://www.blogblog.com/dots/bg_3dots.gif
http://www1.blogblog.com/dots/bg_dots2.gif
http://www1.blogblog.com/dots/bg_dots2.gif
http://www1.blogblog.com/dots/bg_post_title_left.gif
http://www.blogblog.com/dots/icon_comment_left.gif
http://www.blogblog.com/dots/icon_comment_left.gif
http://www.blogblog.com/dots/icon_comment_left.gif
http://www1.blogblog.com/dots/bullet.gif
http://www.blogger.com/navbar.g?targetBlogID=6658540373074530849&amp
http://python25.blogspot.com/2009/10/blog-post_23.html
http://python25.blogspot.com/2009/10/blog-post_23.html
http://python25.blogspot.com/2009/10/blog-post_23.html#comments
http://www.blogger.com/post-edit.g?blogID=6658540373074530849&postID=8238006605508980249
http://www.blogger.com/img/icon18_edit_allbkg.gif
http://python25.blogspot.com/2009/10/blog-post_22.html
http://python25.blogspot.com/2009/10/blog-post_22.html
http://python25.blogspot.com/2009/10/blog-post_22.html#comments
http://www.blogger.com/post-edit.g?blogID=6658540373074530849&postID=5311009012827742139
http://www.blogger.com/img/icon18_edit_allbkg.gif
http://python25.blogspot.com/2009/10/blog-post_21.html
http://python25.blogspot.com/2009/10/blog-post_21.html
http://python25.blogspot.com/2009/10/blog-post_21.html#comments
http://www.blogger.com/post-edit.g?blogID=6658540373074530849&postID=9007033006594304086
http://www.blogger.com/img/icon18_edit_allbkg.gif
http://python25.blogspot.com/2009/10/2.html
http://python25.blogspot.com/2009/10/2.html
http://python25.blogspot.com/2009/10/2.html#comments
http://www.blogger.com/post-edit.g?blogID=6658540373074530849&postID=2181106016910876218
http://www.blogger.com/img/icon18_edit_allbkg.gif
http://python25.blogspot.com/2009/10/mapfilter.html
http://python25.blogspot.com/2009/10/mapfilter.html
http://python25.blogspot.com/2009/10/mapfilter.html#comments
http://www.blogger.com/post-edit.g?blogID=6658540373074530849&postID=2507538441610339075
http://www.blogger.com/img/icon18_edit_allbkg.gif
http://python25.blogspot.com/2009/10/importreload.html
http://www.python.org/dev/peps/pep-0263/
http://python25.blogspot.com/2009/10/importreload.html
http://python25.blogspot.com/2009/10/importreload.html#comments
http://www.blogger.com/post-edit.g?blogID=6658540373074530849&postID=8577865623867738151
http://www.blogger.com/img/icon18_edit_allbkg.gif
http://python25.blogspot.com/2009/10/blog-post_15.html
http://python25.blogspot.com/2009/10/blog-post_15.html
http://python25.blogspot.com/2009/10/blog-post_15.html#comments
http://www.blogger.com/post-edit.g?blogID=6658540373074530849&postID=1242385033646658704
http://www.blogger.com/img/icon18_edit_allbkg.gif
http://python25.blogspot.com/search?updated-max=2009-10-15T06%3A00%3A00%2B09%3A00&max-results=7
http://python25.blogspot.com/feeds/posts/default
http://dame.livedoor.biz
http://www.blogger.com/rearrange?blogID=6658540373074530849&widgetType=LinkList&widgetId=LinkList1&action=editWidget
http://img1.blogblog.com/img/icon18_wrench_allbkg.png
http://python25.blogspot.com/search?updated-min=2009-01-01T00%3A00%3A00%2B09%3A00&updated-max=2010-01-01T00%3A00%3A00%2B09%3A00&max-results=17
http://python25.blogspot.com/2009_10_01_archive.html
http://python25.blogspot.com/2009/10/blog-post_23.html
http://python25.blogspot.com/2009/10/blog-post_22.html
http://python25.blogspot.com/2009/10/blog-post_21.html
http://python25.blogspot.com/2009/10/2.html
http://python25.blogspot.com/2009/10/mapfilter.html
http://python25.blogspot.com/2009/10/importreload.html
http://python25.blogspot.com/2009/10/blog-post_15.html
http://python25.blogspot.com/2009/10/blog-post_14.html
http://python25.blogspot.com/2009/10/blog-post_13.html
http://python25.blogspot.com/2009/10/blog-post_12.html
http://python25.blogspot.com/2009/10/1.html
http://python25.blogspot.com/2009/10/blog-post_08.html
http://python25.blogspot.com/2009/10/blog-post_07.html
http://python25.blogspot.com/2009/10/blog-post_06.html
http://python25.blogspot.com/2009/10/blog-post.html
http://python25.blogspot.com/2009/10/python-1010-10000000000l-101032python.html
http://python25.blogspot.com/2009/10/python.html
http://www.blogger.com/rearrange?blogID=6658540373074530849&widgetType=BlogArchive&widgetId=BlogArchive1&action=editWidget
http://img1.blogblog.com/img/icon18_wrench_allbkg.png
http://www.blogger.com/profile/17765477399828427893
http://4.bp.blogspot.com/_Ar2DPwRuU98/SsijSnhfeUI/AAAAAAAAMhc/BBYQf-3gGrw/S220/yoichiro_pic1.jpg
http://www.blogger.com/profile/17765477399828427893
http://www.blogger.com/rearrange?blogID=6658540373074530849&widgetType=Profile&widgetId=Profile1&action=editWidget
http://img1.blogblog.com/img/icon18_wrench_allbkg.png
https://ssl.
http://www.
http://www.blogger.com/static/v1/widgets/4222249892-widgets.js
http://www.blogger.com/rearrange?blogID=6658540373074530849
http://python25.blogspot.com/,6658540373074530849)
http://www.blogger.com/display?blogID=6658540373074530849
http://python25.blogspot.com/
http://python25.blogspot.com/
http://python25.blogspot.com/feeds/posts/default
http://python25.blogspot.com/feeds/posts/default?alt
http://www.blogger.com/feeds/6658540373074530849/posts/default
http://www.blogger.com/rsd.g?blogID
http://www.blogger.com/profile/17765477399828427893
http://www.blogger.com/openid-server.g
http://img1.blogblog.com/img/icon18_wrench_allbkg.png
http://www.blogger.com/favicon.ico

100%正確な結果が得られたわけではないが、90%以上の精度でURLを抽出できた。

2009年10月26日月曜日

py2exeでEXE化してみた

PythonのスクリプトをEXEファイルに変換してみた。

py2exeから、py2exe-0.6.9.win32-py2.5.exeをダウンロードし、インストール。

・setup.pyを作成

from distutils.core import setup
import py2exe

py2exe_options = {
"compressed": 1,
"optimize": 2,
"bundle_files": 1}

setup(
options = {"py2exe": py2exe_options},
console = [
{"script" : "example.py"}],
zipfile = None)

・コマンドラインから setup.py py2exe

以上の操作で、distフォルダ内にexample.exeが生成されました。


まだ、Pythonを勉強しはじめたばかりで、わからないことだらけですが、あっという間にexeファイルを作成できました。コンソール上で動く簡単なアプリケーションですが、ちゃんと動作しました。単一ファイル書き出しにしたところ2.6MBほどと、大きめのファイルサイズになってしまいましたが、簡単に起動できるのは便利ですね。

参考
Pythonで単体で動くバイナリを作ろう!

2009年10月23日金曜日

アイコンクリックによる起動

すぐに閉じてしまう

Pythonのスクリプトが記述されたファイル(.pyファイル)をダブルクリックすると、Pythonのプログラムを実行できます。しかし、この方法でプログラムを実行すると多くの場合、一瞬で処理が終わりウィンドウが閉じてしまいます。実行結果をファイルなどに保存している場合はそれでもよいですが、実行結果を確認したい場合も多いはずです。

このようなとき、
raw_input()
と書くことでウィンドウが閉じることを防げます。

raw_input関数は、標準入力から1行を読み取る関数ですが、その際にプログラムの実行が止まります。そのため、プログラムの末尾にraw_input()と書いておけば、ウィンドウが自動的に閉じないようになります。

ウィンドウを開かないようにする

拡張子をpywにすると、Windows上で実行した場合も、ウィンドウを開かないようになります。

2009年10月22日木曜日

ディクショナリ型

ディクショナリ型
連想配列みたいなもの。JavaでいうとHashMap型。PHPでいうとarrayでインデックスに文字列を使った状態。

{}で要素が入っていないディクショナリ型のオブジェクトを作成。要素はあとから自由に追加できる。
>>> data = {}
>>> data["a"] = "abc"
>>> data
{'a': 'abc'}

オブジェクトを作らずにいきなり追加はできない。(PHPだとこれでもOK)
>>> data2["a"] = "abc"
Traceback (most recent call last):
File "", line 1, in
NameError: name 'data2' is not defined


最初から要素を入れておくことも可能
>>> data = {"a": "abc", "b": "bcd"}
>>> data
{'a': 'abc', 'b': 'bcd'}

中の要素へのアクセスは以下のようにする。
>>> data['a']
'abc'
>>> data['b']
'bcd'

ディクショナリの入れ子構造も可能
>>> data = {"a":{"b":"c"}}
>>> data["a"]["b"]
'c'

ディクショナリ内にリストを入れることも可能
>>> data = {"a":[1,2,3]}
>>> data["a"][1]
2
>>> data["a"][2]
3

ディクショナリ内に自らを入れることも可能(非推奨)
>>> data = {"a":"b"}
>>> data["b"] = data
>>> data
{'a': 'b', 'b': {...}}

再起構造になっているため特殊な表示形式になっている。こういうデータ構造を作る場合は、無限ループや無限再帰呼び出しが発生しないように十分注意する必要がある。

2009年10月21日水曜日

共有リファレンス

Pythonでは、すべての値はオブジェクトである。変数はオブジェクトに対するリファレンス(C言語のポインタ、Javaの参照のようなもの)を持つ。

>>> a = 1
上記の代入が実行されると、1というオブジェクトが生成され(※)変数aはそのオブジェクトに対するリファレンスを持つ。
>>> b = a
変数bに変数aのリファレンスを代入する。(オブジェクト1を代入するという表現は不適切)

>>> a = a + 1 --- (1)
上記の代入を行うと、aは2になる。
>>> print a
2
だが、bの値は変わらない。
>>> print b
1

(1)では、まずa+1が計算される。その結果として2というオブジェクトが生成され、そのオブジェクトに対するリファレンスが変数bに代入される。(1)が実行されるまでは、aとbは同じオブジェクトに対するリファレンスを持っているが、(1)が実行された瞬間にリファレンスの共有状態が解除されるので注意する必要がある。
この動作は、CやC++になれたプログラマであれば違和感を感じるかもしれないが、一般的には理解しやすいと思う。

実は先ほど取り上げた整数オブジェクトは不変オブジェクトであり、生成された後で値を変更することができないため、共有リファレンスに起因する問題が生じることはない。しかし、すべてのオブジェクトが上記のようにわかりやすい動作をするわけではない。

共有リファレンスが問題になる例
>>> x = [1,2,3]
>>> y = x
>>> x
[1, 2, 3]
>>> y
[1, 2, 3]
>>> x.append(4)
>>> x
[1, 2, 3, 4]
>>> y
[1, 2, 3, 4]

この例では、xに対して行った操作( x.append(4) )がyに影響を与えている。これは、リストが可変型のオブジェクトだからだ。リストの内容は大きくなる場合が多く、不変オブジェクトとして実装した場合、動作速度に問題がでるためこのようになっているそうだが、少し注意して使えば便利な仕様である。Javaでも、Stringクラスは不変、ArrayListなどは可変のオブジェクトとなっているので、あまり違和感はないと思う。

もちろん、この仕様を回避する方法はある。

個別に定義する
>>> a = [1,2,3]
>>> b = [1,2,3]
>>> a.append(4)
>>> a
[1, 2, 3, 4]
>>> b
[1, 2, 3]

スライシングを使ってオブジェクトを複製
>>> a = [1,2,3]
>>> b = a[:]
>>> a.append(4)
>>> a
[1, 2, 3, 4]
>>> b
[1, 2, 3]

※整数値1は、すでに実行環境に存在するはずなので、実際には新しいオブジェクトは生成されない。実行速度を向上させるために、同じ値のオブジェクトがあれば再利用するようになっている。

2009年10月20日火曜日

リスト内包表記/2重ループ

リスト内包表記を使うと2重ループ(のようなもの)も作れます。

>>> [i + j for i in ["1","2","3"] for j in ["a","b","c"]]
['1a', '1b', '1c', '2a', '2b', '2c', '3a', '3b', '3c']

まずi:1に対して、j側のループが回り、その後i:2、i:3に対してループが回っています。左側に置いたループ対象の方が外側のループとして機能します。

九九を計算してみました。でも1つのリストにすべての結果が入っているので、表っぽくないですね。
>>> [i * j for i in range(1,10) for j in range(1,10)]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 2, 4, 6, 8, 10, 12, 14, 16, 18, 3, 6, 9, 12, 15, 18,
21, 24, 27, 4, 8, 12, 16, 20, 24, 28, 32, 36, 5, 10, 15, 20, 25, 30, 35, 40, 45
, 6, 12, 18, 24, 30, 36, 42, 48, 54, 7, 14, 21, 28, 35, 42, 49, 56, 63, 8, 16, 2
4, 32, 40, 48, 56, 64, 72, 9, 18, 27, 36, 45, 54, 63, 72, 81]

こうするとリストのリストになります。
>>> [[i * j for i in range(1,10)] for j in range(1,10)]
[[1, 2, 3, 4, 5, 6, 7, 8, 9], [2, 4, 6, 8, 10, 12, 14, 16, 18], [3, 6, 9, 12, 15
, 18, 21, 24, 27], [4, 8, 12, 16, 20, 24, 28, 32, 36], [5, 10, 15, 20, 25, 30, 3
5, 40, 45], [6, 12, 18, 24, 30, 36, 42, 48, 54], [7, 14, 21, 28, 35, 42, 49, 56,
63], [8, 16, 24, 32, 40, 48, 56, 64, 72], [9, 18, 27, 36, 45, 54, 63, 72, 81]]

適当に整形してあげると、表にみえますよね。
[[1, 2, 3, 4, 5, 6, 7, 8, 9],
[2, 4, 6, 8, 10, 12, 14, 16, 18],
[3, 6, 9, 12, 15, 18, 21, 24, 27],
[4, 8, 12, 16, 20, 24, 28, 32, 36],
[5, 10, 15, 20, 25, 30, 35, 40, 45],
[6, 12, 18, 24, 30, 36, 42, 48, 54],
[7, 14, 21, 28, 35, 42, 49, 56, 63],
[8, 16, 24, 32, 40, 48, 56, 64, 72],
[9, 18, 27, 36, 45, 54, 63, 72, 81]]

もちろん、二重ループの場合も、ifによる抽出条件は使えます。

九九の表から8の倍数だけを抽出。
>>> [i * j for i in range(1,10) for j in range(1,10) if i * j % 8 == 0]
[8, 8, 16, 24, 8, 16, 24, 32, 40, 24, 48, 56, 8, 16, 24, 32, 40, 48, 56, 64, 72,
72]

重複を取り除くにはset型に変更した後、sortedでリストに戻すとよい。
>>> sorted(set([i * j for i in range(1,10) for j in range(1,10) if i * j % 8 ==
0]))
[8, 16, 24, 32, 40, 48, 56, 64, 72]

2009年10月19日月曜日

リスト内包表記/mapとfilterの代替機能

リスト内包表記とは、リストを加工するための表記方法

一番簡単な例。そのまま出力。
>>> [i for i in [1,2,3]]
[1, 2, 3]

リスト内の要素に対して演算を行う例
各要素の2乗からなるリストを返します。
>>> [i**2 for i in [1,2,3]]
[1, 4, 9]

mapを使えば同じことを実現できます。引数の二乗を返す関数をつくって・・・
>>> def square(x): return x ** 2
...
>>> map(square, [1,2,3])
[1, 4, 9]

lambda式を使えば名前空間に関数を作らずに同じことを実現できます。
>>> map((lambda x: x ** 2), [1,2,3])
[1, 4, 9]

中の値を計算して新しいリストを作る場合は、上記の2例のようにmap関数で同様の処理を行えます。ですが、実行速度も、表記の簡潔さもリスト内包表記の方が優れています。


特定の条件を満たす値だけを取り出す例

奇数の要素だけを取り出します。
>>> [i for i in [1,2,3] if i % 2 == 1]
[1, 3]

これと同様の処理はfilter関数でも実現できます。
>>> def isodd(x): return x % 2 == 1
...
>>> filter(isodd, [1,2,3])
[1, 3]

もちろん、lambda式(ラムダ式)を使うこともできます。
>>> filter((lambda x: x % 2 == 1), [1,2,3])
[1, 3]

このように、filterを使うことで、リスト内包表記と同様の処理を行えますが、実行速度も表記の簡潔さもリスト内包表記の方が優れています。

2009年10月16日金曜日

モジュールのimportとreload

Pythonでは、コマンドラインから入力したコマンドだけでなく、テキストファイルに書いたソースファイルをモジュールとしてimportすることもできます。1つのソースファイルを複数回importすることはできないので、モジュールを変更した場合や、モジュールを再実行したい場合はreloadする必要があります。ソjavascript:void(0)ースファイルの拡張子は必ずpyにする必要があります。

モジュールの使用例
以下のスクリプトをhelloworld.pyをカレントディレクトリに保存します。
print "Hello World!!!"

カレントディレクトリはosモジュールのgetcwdメソッドで取得できます。
>>> import os
>>> os.getcwd()
'C:\\Python25'

さっそく、作成したモジュールを使ってみましょう。
>>> import helloworld #定義したモジュールをインポートします
Hello World!!!
>>> import helloworld #すでにインポート済みなので何も起こりません。
>>> reload(helloworld) #リロードすると、再インポートされます。
Hello World!!!
#変更がなかったのでキャッシュからインポート

このように、一連のスクリプトを外部ファイルに保存することで、スクリプトを毎回入力する必要がなくなります。

外部ファイルによる関数の定義
外部ファイルで関数を定義することもできます。外部ファイルで定義した関数はモジュール内にインポートされます。などと、言葉で説明しても伝わらないのでサンプルプログラムで説明します。

dog.pyというモジュールに以下のソースコードを保存します。
# coding=SJIS
def drink():
  print "ごくごく"
※1行目のコメントで、テキストファイルの文字コードを指定します。

使用例
>>> import dog
>>> dog.drink()
ごくごく
>>>
インポートしたすると、drinkというメソッドをもったdogというモジュールが生成されます。dog.drink()で、drinkメソッドを実行することができます。

以下のように、dog.drinkメソッドを変数に代入することもできます。
>>> drink = dog.drink
>>> drink()
ごくごく

2009年10月15日木曜日

関数

Pythonでは関数はdefを使って定義します。

形式 : def [関数名] ([引数]):

関数内では、インデント(行の左端)を右にずらします。
インデントの幅が一定になるように注意しましょう。
※タブでも半角スペースでもかまいませんが、混ぜてはいけません。

>>> def double (number):
...     return number*2
...
>>> double(2)
4
>>> double(4)
8
>>> double(0.5)
1.0
>>> double("hoge")
'hogehoge'

関数も単なるオブジェクトなので、他の変数に代入できます。
>>> twice = double
>>> twice(2)
4
>>> twice(4)
8

昨日作成したHTMLエンコードの例を関数すると、
def encode(str):
  str = str.replace('&','&')
  str = str.replace('<','<')
  str = str.replace('>','>')
  str = str.replace('"','"')
  return str;
となります。

使用例
>>> encode("")
'<html>'

関数を使うようになると、コマンドラインからの入力では厳しいですね。
次回は、importとreloadについて紹介する予定です。

2009年10月14日水曜日

ファイル入出力

改行をbrタグに置換
open("index.html", "w").write(open("index.txt").read().replace("\n","<br>\n"))

HTMLエンコード
html = open("index.txt").read()
html = html.replace('&','&amp;')
html = html.replace('<','&lt;')
html = html.replace('>','&gt;')
html = html.replace('"','&quot;')
open("index.html","w").write(html)
※&のエンコードを最初にしないとうまくいきません。

HTML一括生成
HTMLテンプレートと設定CSVから一括でHTMLを生成
HTMLテンプレート例
<html>
<head><title>[[title]]</title></head>
<body>
<h1>[[title]]</h1>
<p>[[contents]]</p>
</body>
</html>
CSV例
page1.html,page1,This is page one!
page2.html,page2,This is page two!
page3.html,page3,This is page three!
page4.html,page4,This is page four!
ソースコード
>>> for line in open("contents.csv"):
...     filename,title,contents = line.split(",")
...     open(filename,"w").write(open("template").read().replace("[[title]]",title
).replace("[[contents]]",contents))

1行目で、CSVを読み込み1行ずつループを実行
2行目で、CSVをカンマで分割し変数に代入
3行目で、テンプレートを開き、該当箇所を置換した後、保存

2009年10月13日火曜日

ファイル出力

ファイルへの出力
>>> f = open("helloworld.txt", "w")
>>> f.write("Hello World!!")
>>> f.close()
open関数の第2引数に"w"を指定すると書き込みモードでファイルを開きます。
書き込みが確実に実行されるように、必ず最後にcloseを実行しましょう。

ファイルの複製
>>> fr = open("helloworld.txt") # 第2引数のデフォルト値は"r"
>>> fw = open("helloworld_copy.txt", "w")
>>> fw.write(fr.read())
>>> fr.close()
>>> fw.close()

closeしないとどうなるのか?
>>> f = open("helloworld.txt", "w")
>>> f.write("12345")
この段階では、まだファイルは空の状態だった。
>>> f.close()
Closeを実行した瞬間、writeの内容がファイルに書き込まれた。

この結果から判断するとcloseするまで書き込まれないように思われるが、必ずしもそうではない。

以下の例では、すぐに書き込みが実行される。
>>> open("helloworld.txt", "w").write("54321")

また、最初の例を以下のように変更しても書き込みは実行される。
>>> f = open("helloworld.txt", "w")
>>> f.write("12345")
>>> f = ""

Pythonの実装を確認したわけではないが、ファイルオブジェクトへの「参照」がなくなった時点でcloseが自動的に呼び出されているようだ。このことを利用すると、最初にあげた2つの例は以下のように省略できる。

ファイルへの出力
>>> open("helloworld.txt", "w").write("Hello World!!")

ファイルの複製
>>> open("helloworld_copy.txt", "w").write(open("helloworld.txt").read())

明日の記事では、ファイルの入出力を使った実用的な例を紹介しようと思う。

2009年10月12日月曜日

ファイル入力

list.txtに以下のような内容を保存。
10
20
30
40
50

テキストファイルを開き、1行ずつ表示する例。
>>> for line in open("list.txt").readlines():
...     print line
...
10

20

30

40

50

上記の例ではlineの末尾に改行コードが入っているため、printの改行と合わせて2重に改行されてしまう。

Stripメソッドを使うと、無駄な改行を除去できる。
>>> for line in open("list.txt").readlines():
...     print line.strip()
...
10
20
30
40
50

入力値の二乗を表示する例
>>> for line in open("list.txt").readlines():
...     int(line) ** 2
...
100
400
900
1600
2500
※読み込んだ値は"文字列"なので、int関数を使い整数に変換する必要があります。

棒グラフを表示する例
>>> for line in open("list.txt").readlines():
...     print("*" * int(line))
...
**********
********************
******************************
****************************************
**************************************************

数値の合計を求める例
>>> sum = 0
>>> for line in open("list.txt").readlines():
...     sum += int(line)
...
>>> sum
150

最大値を求める例 (入力値に必ず0以上の数値が含まれているものとする)
>>> max = 0
>>> for line in open("list.txt").readlines():
...     if int(line) > max:
...       max = int(line)
...
>>> max
50

2009年10月9日金曜日

ループ(第1回)

ループを説明するまえに、Pythonのプログラムのブロック構造について簡単に紹介しておきます。

C,C++,Java,JavaScript,PHPなどの言語では、プログラムのブロック構造は{}で表します。
1: for(i=0;i<10;i++){
2:   printf("%d", i);
3: }
このC風のプログラムでは、1行目の{と3行目の}の間がループの範囲になります。

同じプログラムをPythonで書くと、
1: for i in range(10):
2:   print i
となります。Pythonでは、インデント(文字の左端の場所のずれ)以外に何も必要ありません。逆にいうと、インデントが文法上大きな意味を持っています。

インデントにはスペース(もちろん半角でお願いします)とタブを使えますが、混ぜると大変なことになるので、どちらか片方を使うようにしましょう。



for in を使うと、リスト内の各要素に対して処理を実行できる。
>>> for x in [1,2,3,4,5]:
...   x
...
1
2
3
4
5

リスト内の値は数字でなくてもよい。
>>> for c in ["a","b","c","d","e"]:
...   c*5
...
'aaaaa'
'bbbbb'
'ccccc'
'ddddd'
'eeeee'

10回ループなどの場合はrange()関数を使うとよい。
>>> for i in range(10):
...   i,i*i
...
(0, 0)
(1, 1)
(2, 4)
(3, 9)
(4, 16)
(5, 25)
(6, 36)
(7, 49)
(8, 64)
(9, 81)

range関数は、実際にはリストを返している。
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

変則的なループ範囲の指定はrange関数の第2引数、第3引数を使えばよい。
>>> range(5,10) #5からスタート
[5, 6, 7, 8, 9]
>>> range(0,10,2) #step 2
[0, 2, 4, 6, 8]
>>> range(9,-1,-1) #step -1
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

2重ループ
>>> for i in range(1,10):
...   for j in range(1,10):
...     print i*j, #最後に,を入れると改行しない
...   print #改行だけする
...
1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
5 10 15 20 25 30 35 40 45
6 12 18 24 30 36 42 48 54
7 14 21 28 35 42 49 56 63
8 16 24 32 40 48 56 64 72
9 18 27 36 45 54 63 72 81

以上でループ(第1回)は終わりです。

2009年10月8日木曜日

文字列(実用編)

前回は、文字列の結合や部分文字列の抽出を行ったが、実際にプログラムを作成するとなると、もっと高機能な文字列操作が必要になる。今回は文字列オブジェクト固有の「便利」なメソッドを紹介する。

find: 特定の文字が最初に出現する位置を返す
>>> "abcde".find("a") ##1文字目は0に
0
>>> "abcde".find("c")
2
>>> "abcde".find("e")
4
>>> "abcde".find("f") ##見つからなければ-1に
-1

upper 大文字に
lower 小文字に
>>> "abcde".upper()
'ABCDE'
>>> "ABCDE".lower()
'abcde'

大文字にして小文字にしたら元通り。(メソッドチェーン)
>>> "abcde".upper().lower()
'abcde'

rstrip: 行末の空白文字の除去
>>> "ABCDE\n"
'ABCDE\n'
>>> "ABCDE\n".rstrip()
'ABCDE'
>>> "ABCDE\n\t".rstrip()
'ABCDE'
>>> "ABCDE ".rstrip()
'ABCDE'
ユーザ入力の処理や、CSVインポート時に、行末のゴミを取り除けます。

split: 文字列を分割しリスト化
>>> "a,b,c".split(",")
['a', 'b', 'c']

startswith: 特定の文字列で始まるか確認
>>> "hello".startswith("H")
False
>>> "hello".startswith("h")
True

文字列オブジェクトの属性は以下のとおり。
>>> dir("hoge")
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__
ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__g
t__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__
', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '
__rmul__', '__setattr__', '__str__', 'capitalize', 'center', 'count', 'decode',
'encode', 'endswith', 'expandtabs', 'find', 'index', 'isalnum', 'isalpha', 'isdi
git', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lst
rip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit'
, 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', '
translate', 'upper', 'zfill']

文字列は非常によく使うオブジェクトです。__で囲まれているのは特殊な属性なので無視するとして、それ以外のメソッドは一通り機能を確認しておくとよいでしょう。

2009年10月7日水曜日

文字列(基礎編)

文字列は文字の列(シーケンス)のこと。

1行の文字列はダブルクオーテーションもしくはクオーテーションで囲む。
>>> print "Hello World!"
Hello World!
>>> print 'Hello World!'
Hello World!
>>> print '"'
"
>>> print "'"
'

複数行の文字列はダブルクオーテーション3個もしくは、シングルクオーテーション3個で囲む。
>>> print """
... Hello World!
... Hello World!
... """
>>> print '''
... Hello World!
... Hello World!
... '''

インデックス指定で文字を抽出できる。
>>> "Hello"[0]
'H'
>>> "Hello"[1]
'e'
>>> "Hello"[2]
'l'
>>> "Hello"[3]
'l'
>>> "Hello"[4]
'o'
変数に代入したほうが分かりやすいかもしれない。
>>> h = "Hello"
>>> h[0]
'H'
>>> h[1]
'e'
>>> h[2]
'l'
>>> h[3]
'l'
>>> h[4]
'o'

文字列の連結は+で行う。
>>> "Hello"+" "+"World!"
'Hello World!'

文字列に整数をかけると文字列の繰り返しになる。
>>> "*"*30
'******************************'

部分文字列の取り出し
>>> "abcde"[0:3]
'abc'
>>> "abcde"[1:3]
'bc'
>>> "abcde"[2:3]
'c'

引数は省略できる
>>> "abcde"[:3] ##最初から3文字
'abc'
>>> "abcde"[:] ##全体を取り出し
'abcde'
>>> "abcde"[2:] ##3文字目から後ろ
'cde'

引数には負の値を指定できる
>>> "abcde"[-3:] ##後ろから3文字
'cde'
>>> "abcde"[-2:] ##後ろから2文字
'de'
>>> "abcde"[-1:]
'e'
>>> "abcde"[-1] ##最後の1文字
'e'

以上の方法は文字列だけでなく、シーケンス型のすべてのオブジェクトに適用できますが、現実的な文字列操作には適していません。(文字列置換をするだけでも大変)次回は、文字列操作専用のメソッドを使ってみます。

2009年10月6日火曜日

乱数モジュール

randomモジュールをインポートすると、乱数関連の機能を使えるようになります。

>>> import random
>>> random.random()
0.40109116074255802
>>> random.random()
0.19860637981500229
>>> random.random()
0.095362165518568798

Pythonを使って、ゲームやシミュレータを開発する場合はお世話になりそうなモジュールですね。randomモジュールには以下のような属性があるようですが、ほとんど意味がわかりません。

>>> dir(random)
['BPF', 'LOG4', 'NV_MAGICCONST', 'RECIP_BPF', 'Random', 'SG_MAGICCONST', 'System
Random', 'TWOPI', 'WichmannHill', '_BuiltinMethodType', '_MethodType', '__all__'
, '__builtins__', '__doc__', '__file__', '__name__', '_acos', '_ceil', '_cos', '
_e', '_exp', '_hexlify', '_inst', '_log', '_pi', '_random', '_sin', '_sqrt', '_t
est', '_test_generator', '_urandom', '_warn', 'betavariate', 'choice', 'expovari
ate', 'gammavariate', 'gauss', 'getrandbits', 'getstate', 'jumpahead', 'lognormv
ariate', 'normalvariate', 'paretovariate', 'randint', 'random', 'randrange', 'sa
mple', 'seed', 'setstate', 'shuffle', 'uniform', 'vonmisesvariate', 'weibullvari
ate']
>>>

randomメソッド以外に、いくつか使ってみました。

choiceメソッド: 与えられた「シーケンス」からランダムな要素を返す
リストはシーケンスの一種なので、引数としてリストを使える。
>>> random.choice([1,2,3,4,5])
3
>>> random.choice([1,2,3,4,5])
1
>>> random.choice([1,2,3,4,5])
5
>>> random.choice([1,2,3,4,5])
3
「タプル」もシーケンスなので、同様に動作。
>>> random.choice((1,2,3,4,5))
4
>>> random.choice((1,2,3,4,5))
2
>>> random.choice((1,2,3,4,5))
3
>>> random.choice((1,2,3,4,5))
3

randrange: 2つの数字の間のランダムな整数を返す
>>> random.randrange(1,10)
6
>>> random.randrange(1,10)
7
>>> random.randrange(1,10)
6
>>> random.randrange(1,10)
8
>>> random.randrange(1,10)
6
ヘルプをみてみると、いろんなオプションが使えるようですが、無暗に使うなと警告されています。
>>> help(random.randrange)
Help on method randrange in module random:

randrange(self, start, stop=None, step=1, int=, default=None, maxwid
th=9007199254740992L) method of random.Random instance
Choose a random item from range(start, stop[, step]).

This fixes the problem with randint() which includes the
endpoint; in Python this is usually not what you want.
Do not supply the 'int', 'default', and 'maxwidth' arguments.

2009年10月5日月曜日

数値モジュール

数値モジュールを使うためには、まずmathモジュールをインポートします。mathモジュールをインポートすると、math.piなどのような定数、math.sqrt()などのようなメソッドを使うことができます。

>>> import math
>>> math.pi
3.1415926535897931
>>> math.sqrt(9)
3.0
>>> math.sqrt(2)
1.4142135623730951

数値モジュールに、どんなメソッドや定数が存在するかは、dir関数を使うとわかります。

>>> dir(math)
['__doc__', '__name__', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh',
'degrees', 'e', 'exp', 'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log'
, 'log10', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh']

他の言語を使いこなしている方であれば、大半の関数と定数の意味は推測できますね。より詳しい説明を読みい場合は、help関数を使いましょう。

>>> help(cos)
Traceback (most recent call last):
File "", line 1, in
NameError: name 'cos' is not defined
>>> help(math.cos)
Help on built-in function cos in module math:

cos(...)
cos(x)

Return the cosine of x (measured in radians).

help関数の引数には、モジュール名を忘れずにつけましょう。

2009年10月4日日曜日

四則演算と長整数型

Pythonは、コマンドラインから利用できます。加減剰余は+-*/、累乗は**です。
>>> 1+1
2
>>> 1-1
0
>>> 1*1
1
>>> 10*10
100
>>> 10**10
10000000000L
10の10乗は32ビットの整数値として表現できないので長整数型に変換されたようです。pythonの長整数型は、桁数の上限はなく、誤差も発生しません。

初めてのPython

初めてのPython(オライリージャパン)を読みつつ、勉強したことをまとめていきます。

初めてのPython自体がPython2.5をターゲットに書かれていること、GoogleAppEngineがPython2.5でないと正常に動作しないことから、このブログではPython2.5を使うことにします。

ダウンロードアドレス
http://www.python.jp/Zope/download/pythoncore


3.0や2.6など、より新しいバージョンもありますが、言語仕様が違いますので2.5のご利用を強くお勧めします。

記事投稿の予定について
 平日(月~金)に1日1記事ペースで続けていければと思っています。