目次へ戻る §14へ戻る §15.1 §15.2 §15.3 §15.4 §15.5 §15.6 §15.7 §16へ進む

15. Python

2005/9/27 - 2005/12/15 (鈴)

前回の予告どおり,今回は Cygwin 上の Python 処理系を取り上げます。 Python は,しばしば Lua や Ruby, Perl などと比較される,いわゆる軽量スクリプト言語の一つです。 読みやすく手間のかからない高水準のデータ型と構文を基礎とし, 幅広い問題領域に即応できる強力なライブラリを標準装備とする, なにかといろいろ間に合う言語ですから,おぼえておく価値があります。 *1 たとえば,16進数を Shift JIS や EUC-JP や utf-8 などいろいろな文字コードで何回も解釈しなおしてみる,ということを, プログラムするまでもなく行編集機能付きの電卓として手軽に行えますから, ほかにすぐ間に合う適当なものがない状況では, 文字化け問題に悩むすべてのプログラマのための 強力な助っ人ツールに早変わりします。

以下,次のように進めます。

15.1 Python のてほどき

Cygwin で Python を利用するには, Interpreters カテゴリにある python パッケージをインストールします。

電卓として使う

Python の初心者が最も初めにおぼえる,そして熟練者が最もよく使う, Python の最も実用的な用途の一つが,電卓です。引数なしに python と打鍵して, 対話セッションに入ります。 プロンプト >>> が現れます。

01:~$ python
Python 2.4.1 (#1, May 27 2005, 18:02:40)
[GCC 3.3.3 (cygwin special)] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

5 + 6 を計算してみます。

>>> 5 + 6

Enter キーを打鍵すると,結果の 11 が表示されて, 再びプロンプト >>> が現れます。

11
>>>

入力中の行に対して,bash と同様の行編集が可能です。 上向き矢印 または Control-P で以前入力した行をさかのぼって再利用できますから, 少しずつ値を変えて計算結果を試す,という操作が自然にできます。 対話セッションを終了するには Control-D を打鍵します。

十進数のほか,C 言語と同様の表記方法で十六進数と八進数が使えます。 組込み関数 hex(n) と oct(n) はそれぞれ n の 十六進表現と八進表現の文字列を返します。十進数の文字列を得るには, 文字列の型オブジェクト str を使います。 別解として,C 言語の printf に準じた書式で文字列を生成する % 演算子 を利用できます。 (% は左項が文字列のとき剰余を計算するかわりに書式化文字列を作ります。 文字列は単一引用符どうし,または二重引用符どうしで囲みます)

>>> 2**10
1024
>>> hex(2**10)
'0x400'
>>> 0x100 + 100 + 0100
420
>>> str(100)
'100'
>>> "0x%x == 0%o == %d" % (16, 16, 16)
'0x10 == 020 == 16'
>>> 

二進数は直接はサポートされていませんが,整数の型オブジェクト int の省略可能な第2引数として基数 2 を指定することで, 二進数の文字列を整数に変換できます。

>>> int('1001')
1001
>>> int('1001', 2)
9
>>> 

数を表す組込み型は, 整数 (自動的に無限多倍長に拡張),倍精度浮動小数点数,複素数 (虚数単位はその筋の分野で伝統の j) です。 それぞれの型オブジェクトは int (無限多倍長整数は long), float, complex です。

>>> (2 + 3j) * 4.5 / 6j
(2.25-1.5j)
>>> 

論理型の定数 TrueFalse は自動的に 1 と 0 に変換されます。 論理型の型オブジェクトは bool です。

>>> True + 3
4
>>> 

ここで型オブジェクトという言葉をいぶかしんでいるかもしれません。 型オブジェクトとは,という抽象概念を表すオブジェクトです。 単にと呼んでもよいのですが,他のオブジェクトと同じく, 自由に関数の引数として渡したり,戻り値として返したりできる点に注目して, さしあたり型オブジェクトと呼びましょう。 Python の言語仕様に関する次の重要な取り決めをおぼえてください。

型オブジェクトは,一般に, その型のインスタンスを構築する関数として働きます。

型と型オブジェクトを同一視すれば, これは数学的な慣習およびいくつかの関数型言語と C++ でおなじみのやりかたです。 ある型から別の型への自然な変換があるとき,取り決めとして,型オブジェクトは, そのような変換をする1引数関数として働きます。 上記の例で int('1001')1001 になったのは,このためです。

任意の値に対し,type を適用すると,その型オブジェクトが得られます。

>>> float(1)
1.0
>>> type(1.0)
<type 'float'>
>>> float
<type 'float'>
>>> type(1.0) == float
True
>>> 

勘の良い人は気づいたかと思いますが, type は実は「型オブジェクト」の型を表す型オブジェクトです。 ですから,上記の例のように1引数関数として使うと, 型オブジェクトへの変換関数となります。 関数として自己適用すると,結果は自分自身になります。

>>> type
<type 'type'>
>>> type(type)
<type 'type'>
>>> type(type(type))
<type 'type'>
>>> 

少し脱線しました。とにかく,型オブジェクトの名前だけ記憶すれば, 構築関数ないし変換関数を個別に丸暗記しなくても,あらかた済みます。 (初期の言語仕様では,型オブジェクトに関するこのような取り決めはなく, 個々別々の変換関数があるだけでした。現在の Python は昔の Python に比べ, より整理された,少数の原則でおぼえることのできる言語になっています。 互換性を保ちつつこのような進化を可能にしたのは,関数も含め, ほとんどすべてがオブジェクトという特徴のおかげといえるでしょう)

主な注意点として

対話セッションでは式の評価だけでなく,変数への代入,関数やクラスの定義 などもできますから,強力な関数電卓として利用できます。

スクリプトとモジュールと標準ライブラリ

もちろん Python は,他のスクリプト言語と同様, テキストファイルに書かれた関数定義やクラス定義を実行することもできます。 たとえば,hello.py というファイルを次の内容で作ります。

#!/usr/bin/env python
# -*- coding: shift_jis -*-
print "こんにちは,世界"

一般的なスクリプト言語の場合と同じく, 次のようにこれをスクリプトとして実行できます。

01:~$ python hello.py
こんにちは,世界
01:~$ ./hello.py
こんにちは,世界
01:~$

ファイルに非 ASCII 文字を含めるときは, 先頭付近で -*- coding: shift_jis -*- のようにエンコーディング宣言をします。 処理系の字句解析はこれにしたがいますから,正しく宣言してあれば, Shift_JIS で "ソソソ" のような文字列を書いても問題なく受理されます。 *2

スクリプトのうち, Python の識別子として適格であるような名前に接尾辞 .py を付けたファイル名のものは, モジュール (module) として使えます。 「hello」は識別子として適格ですから,hello.py はモジュールとして使えます。 モジュールを最初に import したとき,モジュールの文が実行されます

>>> import hello
こんにちは,世界
>>> import hello
>>> 

モジュールはそれ自体,オブジェクトです。

>>> hello
<module 'hello' from 'hello.py'>
>>> 

2回目以降の import は,すでにインタープリタ内に構築されている モジュール・オブジェクトを参照する変数を作る,というだけの意味になります。 これが上記の2回目の import で何も表示されなかった理由です。 このようにモジュール名はファイル名であると同時に変数名として使われますから, Python の識別子として適格なファイル名のスクリプトであることが, モジュールとしての要件になるわけです。

最初の import でモジュールの文が実行されるとき, その文はそのモジュール専用に新しく作られた名前空間の中で実行されます。 つまり,文の副作用として変数や関数やクラスが定義されるとき, それらはすべてモジュールの名前空間に閉じ込められます。

モジュールの外からは, モジュールの名前空間内のオブジェクトを, モジュールの属性 (attribute) として参照できます。 もしも hello.py で val = 1 と変数を定義していたならば, import hello のあと,hello.val として その変数を参照できたわけです。 このようにして Python のモジュールは, いわゆる普通一般の「モジュール」のように機能します。 *3

下記は,スクリプトとしても,モジュールとしても機能するファイルの例です。 ファイル名は tak.py とします。

#!/usr/bin/env python

def tak(x, y, z):
    if y >= x:
        return z
    else:
        return tak(tak(x - 1, y, z),
                   tak(y - 1, z, x),
                   tak(z - 1, x, y))

if __name__ == '__main__':
    j = 0
    for i in [1, 2, 3, 4]:
         j += tak(18, 12, i)
    print j

def 文は関数を定義します。 ここでは tak という3引数の再帰的な関数を定義しています。 第2の if で使われている 組込み変数 __name__ は自動的にその文脈でのモジュール名にセットされます。 スクリプトとして実行されているときや,対話セッションでは '__main__' にセットされます。

ですから,もしもスクリプトとして実行した場合, if __name__ == '__main__': 以下が実行され, tak(18, 12, 1) + tak(18, 12, 2) + tak(18, 12, 3) + tak(18, 12, 4) の値が表示されます。

01:~$ ./tak.py
14
01:~$

モジュールとして利用した場合,import 後, モジュール tak の関数 tak を次のように呼び出すことができます。

>>> import tak
>>> tak.tak(18, 12, 1)
2
>>> 

念のために言えば,ここで tak はモジュール・オブジェクトであり, その属性として参照されている tak.tak が関数オブジェクトです。

>>> tak
<module 'tak' from 'tak.py'>
>>> tak.tak
<function tak at 0x5279cc>
>>> 

ところで,今までの例を実際に実行すると,ディレクトリに hello.pyc と tak.pyc というファイルが作られていることに気づくと思います。 これは Python インタープリタが import 時にモジュールを バイトコンパイルした結果です。 モジュールのファイルのあるディレクトリが書込み可能ならば, このように自動的にバイトコンパイルの結果を残します。 後で再び Python を起動したとき,もしこれがあれば, ファイルの日付比較などの簡単な妥当性検査をした上で, 字句解析や構文解析を省いてこの結果を読み込みます。

/usr/lib/python2.4/ 以下には標準ライブラリのモジュール・ファイル (接尾辞 .py), およびそのバイトコンパイルの結果 (接尾辞 .pyc) が収められています。 接尾辞 .pyo のファイルは, 最適化オプションで起動した場合 (python -O) に対するバイトコンパイル結果です。

01:~$ /bin/ls /usr/lib/python2.4
BaseHTTPServer.py       getopt.pyc          regsub.pyc
BaseHTTPServer.pyc      getopt.pyo          regsub.pyo
BaseHTTPServer.pyo      getpass.py          repr.py
Bastion.py              getpass.pyc         repr.pyc
Bastion.pyc             getpass.pyo         repr.pyo
Bastion.pyo             gettext.py          rexec.py
CGIHTTPServer.py        gettext.pyc         rexec.pyc
CGIHTTPServer.pyc       gettext.pyo         rexec.pyo
CGIHTTPServer.pyo       glob.py             rfc822.py
ConfigParser.py         glob.pyc            rfc822.pyc
…後略…

標準ライブラリのうち,(狭義の) 電卓用途に重要なものは, 実数演算のための math モジュールと 複素数演算のための cmath モジュールですが, これらは実は上記のような Python プログラムのファイルとして は用意されてはいません。 /usr/lib/python2.4/lib-dynload/ にある math.dllcmath.dll がその正体です。 効率のため,C 言語で実装され,共有ライブラリ (といいますか 動的ロード・ライブラリ) として用意されているわけです。

>>> import math
>>> math
<module 'math' from '/usr/lib/python2.4/lib-dynload/math.dll'>
>>> 

ただし,使う立場からは普通のモジュールと特に変わりはありません。 下記は実数と複素数の正弦関数を使った例です。

>>> math.sin(0.5)
0.47942553860420301
>>> import cmath
>>> cmath.sin(0.5)
(0.47942553860420301+0j)
>>>

さらに細かなことを言えば,/usr/lib/python2.4/ 以下にあるものが, 標準ライブラリのすべてではありません。 インタープリタ内部に用意されている組込みモジュールもあります。たとえば

>>> import sys
>>> sys
<module 'sys' (built-in)>
>>>

組込みモジュール sys には, システム固有のパラメタや関数がおさめられています。

>>> sys.platform
'cygwin'
>>> sys.version
'2.4.1 (#1, May 27 2005, 18:02:40) \n[GCC 3.3.3 (cygwin special)]'
>>> sys.path
['', '/usr/lib/python24.zip', '/usr/lib/python2.4', 
'/usr/lib/python2.4/plat-cygwin', '/usr/lib/python2.4/lib-tk',
'/usr/lib/python2.4/lib-dynload', '/usr/lib/python2.4/site-packages']
>>>

ここで属性 sys.path はモジュールの検索パスのリストです。 リスト先頭の空文字列 '' はカレントディレクトリを表します。 tak.py を import できたのはこれのおかげです。

ドキュメントと help と dir

Python に付属するドキュメント類一式は,下記の場所にインストールされています。 標準ライブラリの参照マニュアルもここにあります。

/usr/share/doc/python-2.4.1/

次のようにしてブラウザから HTML 文書を読むと良いでしょう。 (§4.3 では cygstart の同義語として open を使えるようにしました)

01:~$ cygstart /usr/share/doc/python-2.4.1/html/index.html

2005年9月現在, /usr/share/doc/python-2.4.1/cheatsheet の和訳 Python 2.3 早見表 を準備中です。 ぼちぼち仕上げていますので,あまりあてにせずに参考にしてください。

言語としての基本概念や構文規則については,ドキュメント類に含まれる Language Reference にあたる必要がありますが, 個々の関数の仕様程度ならば,対話セッションの中で即座に調べることができます。 たとえば,文字列に対するメソッドの一覧は次のようにして得られます。 (ここでは dir 関数の引数として空文字列 "" を使いましたが, 他の文字列でも,あるいは文字列に対する「型オブジェクト」 str を 引数としても同じ結果が得られます)

>>> dir("")
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', 
(…中略…)
rip', 'replace', 'rfind', 'rindex', 'rjust', 'rsplit', 'rstrip', 
'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 
'translate', 'zfill']

文字列に対する split メソッドの仕様は次のようにして得られます。 (もしも WARNING: terminal is not fully functional が表示されるときは,§14.6 の追記にしたがって環境変数の設定を変更してください)

>>> help("".split)
Help on built-in function split:

split(...)
    S.split([sep [,maxsplit]]) -> list of strings

    Return a list of the words in the string S, using sep as the
    delimiter string.  If maxsplit is given, at most maxsplit
    splits are done. If sep is not specified or is None, any
    whitespace string is a separator.

>>>

モジュール・オブジェクトを引数として dir や help を実行すると, モジュールの属性一覧やマニュアル文書を見ることができます。 対話セッションでそれぞれ import してから, dir(math)help(sys) などとしてみてください。 無引数の dir() も試してみてください。

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

ドキュメンテーション文字列

上記に引き続いて, math モジュールの属性 math.__doc__ の内容を見てください。

>>> math.__doc__
'This module is always available.  It provides access to the\nmathematical funct
ions defined by the C standard.'
>>> 

次に help(math) の内容を見てください。

Help on module math:

NAME
    math

FILE
    /usr/lib/python2.4/lib-dynload/math.dll

MODULE DOCS
    http://www.python.org/doc/current/lib/module-math.html

DESCRIPTION
    This module is always available.  It provides access to the
    mathematical functions defined by the C standard.

FUNCTIONS
    acos(...)
        acos(x)

        Return the arc cosine (measured in radians) of x.

(…以下略…)

dir(math.acos) として,関数 math.acos にも 属性 __doc__ があることを確かめてください。

>>> dir(math.acos)
['__call__', '__class__', '__cmp__', '__delattr__', '__doc__', '__getattribute__
', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '_
_reduce_ex__', '__repr__', '__self__', '__setattr__', '__str__']
>>> 

math.acos.__doc__ の内容を見てください。

>>> math.acos.__doc__
'acos(x)\n\nReturn the arc cosine (measured in radians) of x.'
>>> 

次に help(math.acos) の内容を見てください。 help(math) の内容も見直してください。

>>> help(math.acos)
Help on built-in function acos in module math:

acos(...)
    acos(x)

    Return the arc cosine (measured in radians) of x.

>>> 

文字列属性 __doc__ の内容が help の出力を構成していることが分かると思います。 __doc__ の文字列を ドキュメンテーション文字列 (documentation string) と呼びます。

利用者定義のモジュール,(ここでは説明していませんが) クラス, 関数でもドキュメンテーション文字列を定義できます。 モジュール,クラス本体,関数本体の最初の文として文字列リテラルをおくと, それが自動的に __doc__ 属性の値になります (このようなドキュメンテーション文字列の記述方法は, いくつかの Lisp でもおなじみの機能です)。 実例は標準ライブラリのファイルに見ることができます。

01:/usr/lib/python2.4$ more gzip.py
"""Functions that read and write gzipped files.

The user of the file doesn't have to worry about the compression,
but random access is not allowed."""

# based on Andrew Kuchling's minigzip.py distributed with the zlib module

import struct, sys, time
import zlib
import __builtin__

__all__ = ["GzipFile","open"]

FTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT = 1, 2, 4, 8, 16

READ, WRITE = 1, 2

def U32(i):
    """Return i as an unsigned integer, assuming it fits in 32 bits.

    If it's >= 2GB when viewed as a 32-bit unsigned int, return a long.
    """
    if i < 0:
        i += 1L << 32
    return i
(…以下略…)

注意: Python では, """...""" のような3重の引用符にすると複数行にまたがることができます。

ドキュメンテーション文字列の書き方に強制的な規定はありませんが, いくつかの標準ライブラリのファイルを参照して, おおよその慣習にしたがうのが妥当でしょう。 成文化された目安としては下記の第 4.7.6 節があります。

/usr/share/doc/python-2.4.1/html/tut/node6.html

ドキュメンテーション文字列は日本語で書くこともできます。 その場合は §15.4 のTkinter サンプルプログラム のように Unicode 文字列のリテラルとして書くと, どのプラットフォームにも持っていけます。 ただし,現在の Cygwin と Python の組み合わせは Unicode に関し少々問題があります。 次節ではその問題を解決します。


脚注

*1 ちなみに Python を .NET Framework に移植していた方が, しばらく前にマイクロソフトの中の人になったそうです。 移植された処理系 IronPython が将来 .NET Framework の標準スクリプト言語になる可能性もあるかもしれません。

*2 Shift_JIS で2バイト文字 「ソ」 の2バイト目は「\」ですから, 文字列やコメント中のバイト列を iso8859-1 等の1バイト文字の並びとして 解釈すると,「ソ」の次の文字を誤って「\」でエスケープしてしまいます。 Shift_JIS は,必ずしも utf-8 や euc-jp のように非 ASCII のバイト値を透過させるだけで 正しく文字列やコメントを切り分けることができるわけではありません。 専用の字句解析処理が必要です。 Shift_JIS のような (全体からみれば) 異端のものであっても他とスクリプトファイルを相互運用可能にするために, 今日みるようなエンコーディング宣言に基づく字句解析処理が Python に導入されました。
(もしも euc-* や iso8859-* のような素性の良いものだけを考慮するならば, Unicode 文字列リテラルの解釈にだけエンコーディング宣言の指定を 使えばよいわけですし,実際の言語仕様も危うく,そうなりかけたことがありました。 もしもそうなっていたら Shift_JIS を含むいくつかのエンコーディングに 対して ad hoc な非公式パッチが恒久的に必要になるところでした。しかし, 幸いにしてそのような事態は回避されました ...と他人事のように書きました が,実際ちょっとした苦難の時でした)

具体的には, 字句解析のときプログラム・ファイルのバイト列をエンコーディング宣言に基づいて 解釈し,内部表現としていったん utf-8 に変換します。 ただし,ちょっとした高速化として,元々 utf-8 と宣言してあった場合, 変換の過程をスキップします。 また,もしもファイルの先頭3バイトが utf-8 の Byte Order Mark ('\xef\xbb\xbf') だった場合,エンコーディング宣言自体を省略できます。 utf-8 をえこひいきしているようですが,基本的には ASCII ベースでかつ Unicode ベースであるという前提からの論理的な帰結です。

*3 モジュールそれ自体がオブジェクトであることに注意してください。 hello.val は, たまたま hello がモジュールだったというだけで, 基本的には単なるオブジェクトの属性参照の式です。 モジュールを引数として渡すことも,戻り値として返すことも, 変数に代入することも可能です。 このことを利用すると,ある種のデザインパターンをエレガントに表現できます。

目次へ戻る §14へ戻る §15.1 §15.2 §15.3 §15.4 §15.5 §15.6 §15.7 §16へ進む

Copyright (c) 2005 Oki Software Co., Ltd.