Chapter 2: Python言語
Python言語
Pythonについて
Pythonは高水準で凡用性の高いプログラミング言語です。開発者の生産性と可読性に重きを置いて設計されています。 その設計思想は、プログラマの生産性とコードの読みやすさを強調しています。簡単なセマンティックと基礎的なコマンドを最小限の核となる構文を持ちます。一方で、大規模で総括的な標準ライブラリを有し、多くのオペレーティングシステム(OS)の機能を基礎としたアプリケーション・プログラム・インターフェース(API)を含んでいます。Pythonのコードは最小主義ですが、リンクリスト(list
)、タプル(tuple
)、ハッシュテーブル(dict
)、任意長の整数(long
)などの組み込みオブジェクトを定義しています。
Pythonは、オブジェクト指向(class
)、命令型(def
)、関数型(lambda
)などの複数のプログラミングパラダイムをサポートしています。動的型付けシステムと、参照カウントを利用した自動メモリ管理を有しています(Ruby、Perl、Schemeと同様です)。
Pythonは、1991年にGuido Van Rossumによって初めてリリースされました。非営利のPythonソフトウェア財団が管理する、オープンなコミュニティベースの開発モデルになっています。Python言語を実装している多くのインタプリタとコンパイラがあります。1つはJava(Jython)によるものですが、ここでの簡単な説明ではGuidoによって開発されたC実装に言及します。
PythonのオフィシャルWebサイト [python] では多くのチュートリアル、ライブラリーリファレンスや公式ドキュメントを見ることができます。
上記のドキュメントに加えて、参照 [guido] や参照 [lutz] といった書籍も参考になるかもしれません。
すでにPython言語に精通している場合は、本章を飛ばしてもかまわないでしょう。
Starting up
Microsoft WindowsやMac版のweb2pyのバイナリ配布は、Pythonのインタープリターを同梱しています。
以下のように(DOSプロンプトで)入力するとWindows上で起動することができます:
web2py.exe -S welcome
Mac OSXでは、ターミナルウィンドウで以下のコマンドを入力します(web2py.appと同じフォルダにいる必要があります):
./web2py.app/Contents/MacOS/web2py -S welcome
Linuxや他のUnixコンピュータでは、Pythonが既にインストールされているかを確認し、以下のようにシェルコマンドを入力してください:
python web2py.py -S welcome
もしPython2.5以降がインストールされていない場合は、web2pyを起動する前にpythonのダウンロードとインストールが必要です。
-S welcome
は、welcome アプリケーションのコントローラー内でコマンドが実行されているかのように、対話型のシェルを動作することをweb2pyに指示します。web2pyの対話型のコマンドラインと通常のPythonコマンドラインの違いはこれだけです。
管理インターフェースはアプリケーション毎にWebベースのシェルを提供します。次のURLで "welcome" アプリケーション用のシェルにアクセスできます。
http://127.0.0.1:8000/admin/shell/index/welcome
本章におけるすべての例題は、通常のシェルかWebベースのシェルで試すことができます。
help, dir
Python言語には、組み込みおよびユーザ定義両方の現在のスコープで、定義されたオブジェクトに関するドキュメントを取得する2つのコマンドが用意されています。
たとえば "1" というオブジェクトに関するhelpを尋ねることができます:
>>> help(1)
Help on int object:
class int(object)
| int(x[, base]) -> integer
|
| Convert a string or number to an integer, if possible. A floating point
| argument will be truncated towards zero (this does not include a string
| representation of a floating point number!) When converting a string, use
| the optional base. It is an error to supply a base when converting a
| non-string. If the argument is outside the integer range a long object
| will be returned instead.
|
| Methods defined here:
|
| __abs__(...)
| x.__abs__() <==> abs(x)
...
"1" は整数なので、intクラスとそのすべてのメソッドに関する説明が得られます。上記の例では出力結果が長いため、切り取られています。
同様に、dir
コマンドを用いることで、"1" オブジェクトのメソッドのリストを得ることができます:
>>> dir(1)
['__abs__', ..., '__xor__']
型
Pythonは動的型付け言語です。つまり変数に特定の型はなく、それゆえ宣言する必要がありません。一方で値は特定の型を持っています。以下のように変数に対し、そこに格納された値の型について問い合わせることができます:
>>> a = 3
>>> print type(a)
<type 'int'>
>>> a = 3.14
>>> print type(a)
<type 'float'>
>>> a = 'hello python'
>>> print type(a)
<type 'str'>
Pythonはまた、最初からリストや辞書などのデータ構造を内包しています。
str
Pythonは、ASCII文字列とUnicode文字列の2つの異なる型の文字列の使用をサポートしています。ASCII文字列は'...'、"..."、'..'、"""..."""で区切られたものです。トリプルクォートは、複数行の文字列を区切ります。Unicode文字列は u
で始まり、Unicode文字列がその後に続きます。次のようにエンコーディングを選択することで、Unicode文字列をASCII文字列に変換することができます:
>>> a = 'this is an ASCII string'
>>> b = u'This is a Unicode string'
>>> a = b.encode('utf8')
上記のコマンド実行により、a
の結果はUTF8でエンコードされた文字列を格納するASCII文字列になります。web2pyは意図的に、UTF8でエンコードされた文字列を
また、さまざまな方法で、変数を文字列で記述することが可能です:
>>> print 'number is ' + str(3)
number is 3
>>> print 'number is %s' % (3)
number is 3
>>> print 'number is %(number)s' % dict(number=3)
number is 3
最後の表記は、より明示的でエラーが起きにくいので、推奨される書き方です。
多くのPythonオブジェクトは、例えば数字は、str
または repr
を用いて文字列にシリアライズすることができます。これら2つのコマンドは非常に似ていますが、若干異なる出力結果を生成します。例えば:
>>> for i in [3, 'hello']:
print str(i), repr(i)
3 3
hello 'hello'
ユーザー定義クラスにおいて、str
や repr
は __str__
と __repr__
という特別な演算子を用いて定義/再定義できます。これらは後ほど簡単に説明します。より詳細については、Python公式ドキュメント [pydocs] を参照してください。repr
は常に、元の値を持っています 。
Python文字列のもう1つの重要な特徴は、リストのように反復可能なオブジェクトであるという点です。
>>> for i in 'hello':
print i
h
e
l
l
o
list
Pythonリストの主要なメソッドは、追加(append)、挿入(insert)、削除(delete)です:
>>> a = [1, 2, 3]
>>> print type(a)
<type 'list'>
>>> a.append(8)
>>> a.insert(2, 7)
>>> del a[0]
>>> print a
[2, 7, 3, 8]
>>> print len(a)
4
リストは次のようにスライスできます:
>>> print a[:3]
[2, 7, 3]
>>> print a[1:]
[7, 3, 8]
>>> print a[-2:]
[3, 8]
連結することも可能です:
>>> a = [2, 3]
>>> b = [5, 6]
>>> print a + b
[2, 3, 5, 6]
リストは反復可能です。つまり、それをループで回すことができます:
>>> a = [1, 2, 3]
>>> for i in a:
print i
1
2
3
リストの要素は同じ型にする必要はありません。任意のPythonオブジェクトをとることができます。
リスト内包表記 を使用できる典型的な状況があります。次のコードを考えてみます:
>>> a = [1,2,3,4,5]
>>> b = []
>>> for x in a:
if x % 2 == 0:
b.append(x * 3)
>>> b
[6, 12]
このコードはアイテムリストを処理し、入力リストのサブセットを選択、編集し、新しい結果のリストを作成しています。このコードは次のように、リスト内包表記で完全に置き換えられます:
>>> a = [1,2,3,4,5]
>>> b = [x * 3 for x in a if x % 2 == 0]
>>> b
[6, 12]
tuple
タプルはリストに似ていますが、そのサイズと要素は、リストは変更可能なのに対しタプルは変更不可能です。タプルの要素がオブジェクトである場合、オブジェクトの属性は変更可能です。タプルは丸括弧で区切られます。
>>> a = (1, 2, 3)
したがって、以下のコードはリストに対しては動作しますが:
>>> a = [1, 2, 3]
>>> a[1] = 5
>>> print a
[1, 5, 3]
タプルに対しては、要素の割り当てが機能しません:
>>> a = (1, 2, 3)
>>> print a[1]
2
>>> a[1] = 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
タプルはリストと同様に反復可能なオブジェクトです。注意として、単一の要素でタプルを構成する際は、以下のように末尾にコンマをつけなければいけません:
>>> a = (1)
>>> print type(a)
<type 'int'>
>>> a = (1,)
>>> print type(a)
<type 'tuple'>
タプルはその不変性と括弧の利用が省略可能であることから、オブジェクトを効率的に格納するのにとても便利です:
>>> a = 2, 3, 'hello'
>>> x, y, z = a
>>> print x
2
>>> print z
hello
dict
Pythonの dict
-ionary(訳注:dictは辞書の頭文字) は、キーオブジェクトを値オブジェクトにマッピングするハッシュテーブルです。例えば:
>>> a = {'k':'v', 'k2':3}
>>> a['k']
v
>>> a['k2']
3
>>> a.has_key('k')
True
>>> a.has_key('v')
False
キーは任意の型のハッシュ可能な型(int、string、他、__hash__
メソッド実装したクラスのオブジェクト)を取ることができます。値は任意の型を使用できます。同じ辞書内の異なるキーと値は、同じ型にする必要はありません。キーが英数字の場合は、辞書は次のような代替の構文を用いて宣言することができます:
>>> a = dict(k='v', h2=3)
>>> a['k']
v
>>> print a
{'k':'v', 'h2':3}
便利なメソッドは、has_key
、 keys
、values
、items
です:
>>> a = dict(k='v', k2=3)
>>> print a.keys()
['k', 'k2']
>>> print a.values()
['v', 3]
>>> print a.items()
[('k', 'v'), ('k2', 3)]
items
メソッドはタプルのリストを生成し、各タプルはキーとそれに対応付けられた値を格納しています。
辞書の要素とリストの要素は、del
コマンドで削除することができます:
>>> a = [1, 2, 3]
>>> del a[1]
>>> print a
[1, 3]
>>> a = dict(k='v', h2=3)
>>> del a['h2']
>>> print a
{'k':'v'}
内部的には、Pythonは、hash
演算子でオブジェクトを整数に変換し、その整数によってどこに値を格納するかを決めています。
>>> hash("hello world")
-1500746465
インデントについて
Pythonはインデントをコードブロックの区切りに利用しています。ブロック1つはコロンで終了する行から始まって、同じかそれより高いインデントが次の行として現れるまで続きます。例えば:
>>> i = 0
>>> while i < 3:
>>> print i
>>> i = i + 1
>>>
0
1
2
各インデントのレベルには4つのスペースを使うのが一般的です。 (見えない)混乱をもたらす可能性があるため、スペースとタブを混在しなようにすることが良いポリシーです。
for...in
Pythonでは反復可能なオブジェクトをループで回すことができます:
>>> a = [0, 1, 'hello', 'python']
>>> for i in a:
print i
0
1
hello
python
xrange
は一般的なショートカットの1つで、全てのリスト要素を格納せずに、反復可能な範囲オブジェクトを生成します。
>>> for i in xrange(0, 4):
print i
0
1
2
3
これは、次のC / C + + / C#の/ Javaの構文と等価です:
for(int i=0; i<4; i=i+1) { print(i); }
もう1つの有用なコマンドは enumerate
です。次のようにカウントしながらループ処理を行います:
>>> a = [0, 1, 'hello', 'python']
>>> for i, j in enumerate(a):
print i, j
0 0
1 1
2 hello
3 python
また、range(a, b, c)
というキーワードがあります。これは、a
で始まり c
ずつ増加し、b
より小さい最後の値で終わる整数のリストを返します。a
はデフォルト値で0で、c
はデフォルトで1です。xrangeも似ていますが実際にリストを作ることはなく、そのリストに対するイテレータを生成します。これはループ処理にとってより良いです。
break
を利用してループから抜けることができます。 You can jump out of a loop using break
>>> for i in [1, 2, 3]:
print i
break
1
continue
を利用して、全てのコードブロックを実行せずに、次のループ処理へ飛ぶことができます。
>>> for i in [1, 2, 3]:
print i
continue
print 'test'
1
2
3
while
Pythonでは while
ループは他の多くのプログラミング言語と同じように、無限にループ処理を行い、各反復の前に条件のテストを行います。条件が False
になると、ループは終わります。
>>> i = 0
>>> while i < 10:
i = i + 1
>>> print i
10
Pythonでは loop...until
構文はありません。
if...elif...else
>>> for i in range(3):
>>> if i == 0:
>>> print 'zero'
>>> elif i == 1:
>>> print 'one'
>>> else:
>>> print 'other'
zero
one
other
"elif" は "else if" の意味です。elif
句と else
句はどちらも省略可能です。elif
は何回も利用可能ですが、else
は一度だけです。 複雑な条件文はnot
、and
、or
の演算子を利用し作成することができます。
>>> for i in range(3):
>>> if i == 0 or (i == 1 and i + 1 == 2):
>>> print '0 or 1'
try...except...else...finally
>>> try:
>>> a = 1 / 0
>>> except Exception, e:
>>> print 'oops: %s' % e
>>> else:
>>> print 'no problem here'
>>> finally:
>>> print 'done'
oops: integer division or modulo by zero
done
例外が発生した時は、実行が except
句で捕捉されすが、else
句は実行されません。例外が発生しなかった場合は、except
句は実行されず、else
句が実行されます。finally
句は常に実行されます。
異なる例外が発生する可能性のために、複数の except
句がある場合もあります:
>>> try:
>>> raise SyntaxError
>>> except ValueError:
>>> print 'value error'
>>> except SyntaxError:
>>> print 'syntax error'
syntax error
else
句と finally
句は省略可能です。
以下は、組み込みのPython例外のリストと、(web2pyで定義されている)HTTP例外です。
BaseException
+-- HTTP (defined by web2py)
+-- SystemExit
+-- KeyboardInterrupt
+-- Exception
+-- GeneratorExit
+-- StopIteration
+-- StandardError
| +-- ArithmeticError
| | +-- FloatingPointError
| | +-- OverflowError
| | +-- ZeroDivisionError
| +-- AssertionError
| +-- AttributeError
| +-- EnvironmentError
| | +-- IOError
| | +-- OSError
| | +-- WindowsError (Windows)
| | +-- VMSError (VMS)
| +-- EOFError
| +-- ImportError
| +-- LookupError
| | +-- IndexError
| | +-- KeyError
| +-- MemoryError
| +-- NameError
| | +-- UnboundLocalError
| +-- ReferenceError
| +-- RuntimeError
| | +-- NotImplementedError
| +-- SyntaxError
| | +-- IndentationError
| | +-- TabError
| +-- SystemError
| +-- TypeError
| +-- ValueError
| | +-- UnicodeError
| | +-- UnicodeDecodeError
| | +-- UnicodeEncodeError
| | +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
各項目の詳細については、Python公式ドキュメントを参照してください。
web2pyは、HTTP
と呼ばれる新しい例外を1つだけ公開しています。その例外が発生すると、プログラムはHTTPエラーページを返すようになります(詳細は第4章を参照してください)。
どのオブジェクトも例外を発生させることができますが、それには組み込みのexceptionクラスを拡張したオブジェクトの利用を推奨します。
def...return
関数は def
を使って宣言されます。これは典型的なPythonの関数です:
>>> def f(a, b):
return a + b
>>> print f(4, 2)
6
引数や戻り値の型を指定する必要(もしくは方法)はありません。この例では関数 f
は2つの引数を取るように定義されています。
スコープ や 名前空間 の概念をこの章で紹介するのに、関数は最初に説明するコード構文の機能です。上記の例で、a
と b
の変数は、f
関数のスコープの外では未定義になります:
>>> def f(a):
return a + 1
>>> print f(1)
2
>>> print a
Traceback (most recent call last):
File "<pyshell#22>", line 1, in <module>
print a
NameError: name 'a' is not defined
関数のスコープの外で定義された変数は、関数内でも使用可能です。次の例で、変数 a
がどのように扱われているかを検証してみてください:
>>> a = 1
>>> def f(b):
return a + b
>>> print f(1)
2
>>> a = 2
>>> print f(1) # new value of a is used
3
>>> a = 1 # reset a
>>> def g(b):
a = 2 # creates a new local a
return a + b
>>> print g(2)
4
>>> print a # global a is unchanged
1
もし a
が変更されると、その後の関数呼び出しではグローバル変数 a
の新しい値を使用します。これは関数定義が宣言時の変数 a
の値ではなく、変数 a
の保管場所と紐づいているためです。しかし、g
関数内部で a
に値を入れた場合、新しいローカル変数 a
がグローバル変数を隠すため、グローバル変数 a
は変化しません。外部スコープの参照は、クロージャ の作成に使用することが可能です:
>>> def f(x):
def g(y):
return x * y
return g
>>> doubler = f(2) # doubler is a new function
>>> tripler = f(3) # tripler is a new function
>>> quadrupler = f(4) # quadrupler is a new function
>>> print doubler(5)
10
>>> print tripler(5)
15
>>> print quadrupler(5)
20
関数 f
が新しい関数を作成し、g
のスコープは完全にf
の内部であることに注意してください。クロージャは非常に強力です。
関数の引数はデフォルト値を取ることができ、複数の結果を返すことができます:
>>> def f(a, b=2):
return a + b, a - b
>>> x, y = f(5)
>>> print x
7
>>> print y
3
引数を明示的に名称で渡すこともできます。これは呼び出し側で指定された引数の順序が、関数で定義された引数の順序と異なってもよいことを意味します:
>>> def f(a, b=2):
return a + b, a - b
>>> x, y = f(b=5, a=2)
>>> print x
7
>>> print y
-3
関数はまた、可変長の引数を取ることができます:
>>> def f(*a, **b):
return a, b
>>> x, y = f(3, 'hello', c=4, test='world')
>>> print x
(3, 'hello')
>>> print y
{'c':4, 'test':'world'}
ここで、名前付きで渡されなかった引数 (3、'hello') はリスト a
に格納され、名前付きで渡された (c
と test
) は辞書 b
に格納されます。
逆にリストやタプルは、展開しながら、個別の位置引数を要求する関数に渡すことができます:
>>> def f(a, b):
return a + b
>>> c = (1, 2)
>>> print f(*c)
3
辞書もまた、展開しながら、キーワード引数に引き渡すことができます:
>>> def f(a, b):
return a + b
>>> c = {'a':1, 'b':2}
>>> print f(**c)
3
lambda
lambda
を使用することで、とても簡単で簡潔な無名関数を作成することができます:
>>> a = lambda b: b + 2
>>> print a(3)
5
"lambda
[a]:[b]" 式は、文字通り "引数 [a] を持ち [b] を返す関数" として読みます。lambda
式自体は無名関数ですが、変数 a
に割り当てることで名前を獲得します。def
のスコープのルールは、lambda
にも同様に適用されます。上記のコードでの a
の箇所は、次のように def
を使用した関数の宣言と等しくなります:
>>> def a(b):
return b + 2
>>> print a(3)
5
lambda
の長所は簡潔さのみです。しかし簡潔さは、ある状況で非常に便利です。リスト上の要素全てに関数を適用し、新しいリストを作成する map
と呼ばれる関数を考えてみます:
>>> a = [1, 7, 2, 5, 4, 8]
>>> map(lambda x: x + 2, a)
[3, 9, 4, 7, 6, 10]
lambda
の代わりに def
を使用した場合、コード量は2倍になります。lambda
の(Pythonの実装での)主な欠点は、単一式しか許可しない点です。より長い記述の関数の場合は def
使用することで、関数名を用意する余分なコストが、関数の記述量が増えるに従って減少します。
def
と同様に、lambda
を カリー化 関数として使用できます: 既存の関数をラップした新しい関数を作成することで、新しい関数が異なる引数セットを持つことができます:
>>> def f(a, b): return a + b
>>> g = lambda a: f(a, 3)
>>> g(2)
5
カリー化が役立つ状況はたくさんありますが、その中の一つに直接web2pyで有用な、キャッシュがあります。引数が素数かどうかを確認する、次のようなコストの掛かる関数を考えてみます:
def isprime(number):
for p in range(2, number):
if (number % p) == 0:
return False
return True
この関数は明らかに時間が掛かります。
key、関数、秒数の3つの引数を受け取る、cache.ram
というキャッシュ関数があると仮定しましょう。
value = cache.ram('key', f, 60)
初回の実行時は f()
関数を呼び出し、メモリ上の辞書(仮に "d" とします)に結果を保存し値を返します。このため値は以下のようになります:
value = d['key']=f()
2回目の実行時は、もしkeyが辞書に存在し、指定された秒数(60)よりデータが古くない場合、関数を実行しないで対応する値を返します。
value = d['key']
入力値に対応する isprime 関数の結果を、キャッシュするにはどのようにすればよいでしょうか? これは以下のようになります:
>>> number = 7
>>> seconds = 60
>>> print cache.ram(str(number), lambda: isprime(number), seconds)
True
>>> print cache.ram(str(number), lambda: isprime(number), seconds)
True
結果は同じですが、初回に cache.ram
が実行された場合だけ isprime
が呼ばれ、2回目は呼ばれません。
def
やlambda
で作成されたPython関数は、異なる引数の組み合わせの条件で、既存の関数をリファクタリングすることができます。cache.ram
とcache.disk
は、web2pyのキャッシュ関数です。
class
Pythonは動的型付けなので、Pythonのクラスとオブジェクトは少し変わったものに見えるかもしれません。実際、クラスを宣言する際にメンバ変数(属性)を必ずしも定義する必要はなく、同じクラスから作られた異なるインスタンスはそれぞれ違うメンバ変数(属性)を持つことができます。一般的に属性は、クラスではなくインスタンスに関連付けられます(ただし "クラス属性" として宣言された場合は別で、C++/Javaでの "静的メンバ変数" と同じです)。
例を示します:
>>> class MyClass(object): pass
>>> myinstance = MyClass()
>>> myinstance.myvariable = 3
>>> print myinstance.myvariable
3
pass
は、何もしないコマンドであることに注意してください。この場合では、何も含まない MyClass
クラスを定義するために使用されます。MyClass()
はクラスのコンストラクタを呼び出し(この場合はデフォルトのコンストラクタ)、オブジェクト、つまり、このクラスのインスタンスを返します。クラス定義における (object)
の部分は、このクラスが組み込みの object
クラスを拡張したものであることを示しています。これは必須ではないですが、推奨される書き方です。
次により複雑なクラスの例を示します:
>>> class MyClass(object):
>>> z = 2
>>> def __init__(self, a, b):
>>> self.x = a
>>> self.y = b
>>> def add(self):
>>> return self.x + self.y + self.z
>>> myinstance = MyClass(3, 4)
>>> print myinstance.add()
9
クラス内部で定義された関数はメソッドです。幾つかのメソッドは、特別な予約済みの名前を持ちます。例えば、__init__
はコンストラクタです。全ての変数は、メソッドの外で定義されたものでない限り、メソッドのローカル変数です。例えば、z
はクラス変数です。これはC++の静的メンバ変数と同じで、そのクラスの全てのインスタンスに対して同じ値を保持します。
__init__
は3つの引数を取り、add
は1つの引数を取っています。またそれらは、それぞれ2つの引数と0個の引数によって、呼び出されている点に注意してください。最初の引数は慣例により、メソッド内で利用する、現在のオブジェクト参照のローカル名を示します。ここでは self
を現在のオブジェクトを参照するのに利用していますが、他の名前を使用することも可能です。self
はC++の *this
や、Javaの this
と同じ役割を担いますが、self
は予約語ではありません。
この構文は、別のクラス内のメソッドに対して、ローカルのクラスなどの入れ子となったクラスを宣言する時に、曖昧さを避けるために必要です。
特殊属性、メソッド、演算子
2つのアンダスコアから始まるクラス属性、メソッド、演算子は、一般にプライベート(クラス内でのみ使用し、クラス外から呼び出されない)であることを意図しますが、これはインタプリタによって強制される慣例ではありません。
その内の幾つかは予約されたキーワードで、特別な意味を持っています。
例として、その中の3つを示します:
__len__
__getitem__
__setitem__
これらは例えば、リストのように振舞うコンテナオブジェクトの作成に利用可能です:
>>> class MyList(object):
>>> def __init__(self, *a): self.a = list(a)
>>> def __len__(self): return len(self.a)
>>> def __getitem__(self, i): return self.a[i]
>>> def __setitem__(self, i, j): self.a[i] = j
>>> b = MyList(3, 4, 5)
>>> print b[1]
4
>>> b.a[1] = 7
>>> print b.a
[3, 7, 5]
他の特殊演算子としては、クラス属性の取得と設定を定義する __getattr__
と __setattr__
や、算術演算子をオーバーロードする __sum__
や __sub__
などがあります。 これらの演算子の利用については、より高度な書籍を参照してください。また __str__
と __repr__
演算子については、既に言及しました。
ファイル入力/出力
Pythonでは以下のように、ファイルをオープンし書き込むことができます:
>>> file = open('myfile.txt', 'w')
>>> file.write('hello world')
>>> file.close()
同様に以下のように、ファイルから読み出すことができます:
>>> file = open('myfile.txt', 'r')
>>> print file.read()
hello world
別の方法として、"rb" でバイナリモードで読むことができ、"wb" でバイナリモードで書き込むことが可能です。そして、追記モード "a" でファイルをオープンできます。標準のC言語表記を使用します。
read
コマンドは、バイト数のオプション引数を取ります。また seek
を利用し、ファイル内の任意の箇所にジャンプすることができます。
read
でファイルを読み出すことができます:
>>> print file.seek(6)
>>> print file.read()
world
ファイルをクローズは、次のようにします:
>>> file.close()
CPythonとして知られるPythonの標準実装では、変数は参照カウントを使用しており、保持しているファイルハンドルもこれに含まれます。このためCPythonは、オープンのファイルハンドルの参照カウントがゼロになると、ファイルはクローズされていると判断し変数を破棄します。しかしPyPyなどの別の実装では、参照カウントの代わりにガベージコレクションが使用されます。これはもし一度に、大量のオープン・ファイルハンドルが蓄積した場合、gc (ガベージコレクション)がそれらをクローズし破棄する前に、エラーが発生する可能性があります。このため不要になった時に、明示的にファイルをクローズすることが推奨されます。web2py は、read_file()
と write_file()
の2つのヘルパ関数を提供します。これは gluon.fileutils
名前空間内で、ファイルアクセスをカプセル化し、使用されたファイルハンドルが適切にクローズされるのを保証します。
web2pyを使用している時、カレントディレクトリの場所を知る必要はありません。なぜならそれは、web2pyの設定に依存するためです。
request.folder
変数は、現在のアプリケーションへのパスを保持しています。パスは後述するos.path.join
コマンドで、連結することが可能です。
exec
, eval
Javaと異なり、Pythonは真のインタプリンタ言語です。これは、文字列として格納されたPythonコードを、実行する能力があることを意味しています。例えば:
>>> a = "print 'hello world'"
>>> exec(a)
'hello world'
何が起こったのでしょうか?。関数 exec
は自分自身を呼び出し、引数として渡された文字列の内容を実行するよう、インタープリンタに指示します。また、辞書のシンボルで定義されたコンテキスト内の、文字列の内容を実行することも可能です:
>>> a = "print b"
>>> c = dict(b=3)
>>> exec(a, {}, c)
3
ここではインタプリタが、文字列 a
を実行した時に、c
で定義されたシンボル(この例では b
)を参照しています。ただし、c
や a
自身を参照することはありません。これは、exec
が内部コードのできることを制限しないので、制限された環境とは異なります。コード対し、利用可能な変数セットを定義しているだけです。
関連する関数として eval
があります。これは exec
と非常に似た動きをしますが、引数の処理結果が値になることを期待し、その値を返すという点で異なります。
>>> a = "3*4"
>>> b = eval(a)
>>> print b
12
import
例えば、乱数を作成する必要がある場合、次のようにできます:
>>> import random
>>> print random.randint(0, 9)
5
これは、0~9(9を含む)の間のランダムな整数、例では5を出力します。randint
関数は、random
モジュール内で定義されています。モジュールからオブジェクトを、現在の名前空間にインポートすることも可能です:
>>> from random import randint
>>> print randint(0, 9)
もしくは、モジュールから全てのオブジェクトを現在の名前空間にインポートすることも可能です:
>>> from random import *
>>> print randint(0, 9)
もしくは、全てを新しく定義した名前空間にインポートすることも可能です:
>>> import random as myrand
>>> print myrand.randint(0, 9)
本書の残りの部分では、os
、sys
、datetime
、time
、cPickle
といったモジュールに定義されたオブジェクトを主に利用します。
全てのweb2pyオブジェクトは、
gluon
と呼ばれるモジュールを介してアクセスすることができます。これは後の章で説明します。内部的には、web2pyは多くのPythonモジュール(例えばthread
)を使用していますが、利用者が直接それらにアクセスする必要はほとんどありません。
次のサブセクションでは、最も利用されるそれらのモジュールを考えます。
os
このモジュールは、オペレーティング・システムAPIへのインターフェイスを提供します。例えば:
>>> import os
>>> os.chdir('..')
>>> os.unlink('filename_to_be_deleted')
chdir
のようなos
の幾つかの関数は、web2pyで使用しないでください。なぜなら、スレッドセーフではないからです。
os.path.join
はとても便利で、OSに依存しない形でパスの連結が可能になります:
>>> import os
>>> a = os.path.join('path', 'sub_path')
>>> print a
path/sub_path
システム環境変数は、次を介してアクセス可能です:
>>> print os.environ
これは読み取り専用の辞書です。
sys
sys
モジュールは多くの変数と関数を持ちますが、最も利用するのは sys.path
です。これは、Pythonがモジュールを探すためのパスのリストを保持しています。モジュールをインポートしようとした時、Pythonは sys.path
内にリストされた全てのフォルダをチェックします。幾つかのロケーションに追加のモジュールをインストールし、それらをPythonに探させたい場合、そのロケーションへのパスを sys.path
に追加する必要があります。
>>> import sys
>>> sys.path.append('path/to/my/modules')
web2pyが実行されている時、Pythonはメモリに常駐し、そしてHTTPリクエストのサービスを行う多くのスレッドがあるとはいえ、sys.path
は1つしかありません。メモリリークを回避するにためには、パスを追加する前に、そのパスが既にあるかを確認するのがベストです:
>>> path = 'path/to/my/modules'
>>> if not path in sys.path:
sys.path.append(path)
datetime
datetimeモジュールの使い方を説明する幾つかの例を紹介します:
>>> import datetime
>>> print datetime.datetime.today()
2008-07-04 14:03:90
>>> print datetime.date.today()
2008-07-04
時にはローカルタイムではなく、UTCタイムに基づいたタイムスタンプデータが必要になるかもしれません。その場合、次の関数を使用することができます:
>>> import datetime
>>> print datetime.datetime.utcnow()
2008-07-04 14:03:90
datetimeのモジュールには、date、datetime、time、timedelta など、さまざまなクラスが含まれています。2つのdate、もしくは2つのdatetime、もしくは2つのtimeオブジェクト間の差は、timedeltaになります:
>>> a = datetime.datetime(2008, 1, 1, 20, 30)
>>> b = datetime.datetime(2008, 1, 2, 20, 30)
>>> c = b - a
>>> print c.days
1
web2pyでデータベースへ渡す時や戻される時に、dateとdatetimeは、対応するSQLの型を格納するのに使用されます。
time
timeモジュールは date
や datetime
と異なり、(1970年から始まる)エポックからの秒数として時間を表現します。
>>> import time
>>> t = time.time()
1215138737.571
秒時間と datetime
時間との変換関数については、Pythonドキュメントを参照してください。
cPickle
cPickleはとても強力なモジュールです。cPickleは自己参照オブジェクトを含む、ほぼ全てのPythonオブジェクトをシリアライズすることができる関数を提供します。例えば、次のような奇妙なオブジェクトを構築します:
>>> class MyClass(object): pass
>>> myinstance = MyClass()
>>> myinstance.x = 'something'
>>> a = [1 ,2, {'hello':'world'}, [3, 4, [myinstance]]]
そして以下のようにします:
>>> import cPickle
>>> b = cPickle.dumps(a)
>>> c = cPickle.loads(b)
この例では b
は a
の文字列表現で、c
は b
をデシリアライズ(訳注: 逆シリアライズ)によって生成した a
のコピーです。
cPickleは、ファイルへシリアライズ、ファイルからデシリアライスすることも可能です:
>>> cPickle.dump(a, open('myfile.pickle', 'wb'))
>>> c = cPickle.load(open('myfile.pickle', 'rb'))