FrontPage  Index  Search  Changes  RSS  Login

[python]基礎

はじめに

2007/02/03(Sat)より、書籍『初めてのPython 第2版』(オライリー・ジャパン)を参考にPythonに触れます。その中で気になった点などを整理する目的で記録します。

  • 2007/02/11(Sun)で一区切りつけました

対話モード

Pythonでも対話モードシェルが起動可能で、python実行バイナリを引数無しで実行した場合に対話モードとなる。

JITコンパイラ

PsycoというPython用のJIT(Just In a Time)コンパイラに関するプロジェクトが存在し、このJITコンパイラによって2〜100倍の処理速度向上が確認されたとの事。

Pythonインタプリタによる実行

  1. バイトコードへのコンパイル
  2. PVM(Python Virtual Machine)で実行

フローズンバイナリによる配布

Pythonで記述したアプリケーションを、そのバイトコード/PVM(インタプリタ)/他などを含めて一つの実行形式ファイルに変換する事が出来る。

インテグレーション

C言語やC++/JAVAなど、他言語間での利用が可能。

Pythonシステム

CPython

ANSI Cによる実装。基本的にCPythonが標準のPython。

Jython

PythonとJAVAとの統合を目的としたバリエーション。

Python.NET

Pythonと.NETフレームワークを利用したアプリケーションとの統合を目的としたバリエーション。

注目キーワード

  • コルーチン

長整数

Pythonでは、メモリが許す限りの桁数を扱う事が出来る。短整数でオーバーフローを起こした場合も、自動的に長整数に変換される(v2.2以降)。

型情報

Pythonでは型情報を変数ではなくオブジェクトがもつ。

数値演算

整数同士の除算

/演算子の場合、小数点以下切り捨てられる。v3.0以降では、オペランドの型が何であっても切り捨てられなくなるとの事。//演算子を利用した場合は、オペランドの型が何であれ、演算結果の小数点以下の桁数がオペランドより増える事は無い。

変数

変数はオブジェクトへのリファレンス。変数に再代入が行われた場合、現在参照されているオブジェクトが変更されるわけではなく、参照するオブジェクトを切り替える事になる(不変性; 値を変更可能なオブジェクトもある)。

値を変更可能なオブジェクト

例えば、リストは値を変更可能なオブジェクト。ただし、値を変更する場合は、共有リファレンスの存在に注意する必要がある。

文字列

クォーテーションとエスケープシーケンス

Pythonでは、文字列を囲むクォーテーションがシングルクォーテーションでもダブルクォーテーションでも同じである(他の言語では、エスケープシーケンスの扱いに違いがある場合がある)。

raw文字列

raw文字列で表現する事で、文字列中のエスケープシーケンスを解釈せず、そのままの文字として扱う事が出来る。

トリプルクォーテーション(ブロック文字列)

3つ重ねたクォーテーションで囲んだ文字列は、複数行に分けて記述する事が出来る。

オーバーロードされた演算子

文字列用に演算子がオーバーロードされている(異なる型との間ではエラーとなる; 文字列と数値をそのまま+演算子で連結させる事は出来ない)。

+

連結

*

繰り返し

%

文字列フォーマット

スライシングを利用した文字列コピー

[:]を利用する事で、文字列全てをコピーする事が出来る。

フォーマット

%[(キー)][フラグ][桁数][.精度]コード

型のカテゴリー

型のカテゴリーが一緒であれば、ほぼ同じ演算(操作)を行う事が出来る。

数値

四則演算を行う事が出来る。

シーケンス

インデクシング、スライシング、連結などを行う事が出来る。

写像(マッピング)

キーによるインデクシングなどが行える。

ディクショナリ

  • キーは不変性のオブジェクト

スパース行列

リストと異なり、ディクショナリではキーがまばらである行列などを簡単に扱う事が出来る(リストでは、未定義のキーに代入する事は出来ない)。

matrix = {}
matrix[(2,3,4)] = 88

上記のように、タプルをキーとして座標を表現する事も簡単に出来る。

タプル

  • オブジェクト(へのリファレンス)を一定の順序で並べたもの
  • 不変性シーケンスの一種

不変性を持っているため、ディクショナリのキーに利用出来る。また、不変性を持つオブジェクトのみが対象となるビルトイン演算子を適用可能である。

コピー

オブジェクトのコピーは、スライシングやcopyメソッドを利用して行う事が可能。ただし、トップレベル要素のみがコピーの対象となる点に注意(copyモジュールをインポートしてcopy.deepcopyメソッドを利用すれば、ネストされたオブジェクトもコピーの対象とする事が出来る)。

オブジェクトの比較

オブジェクトの比較は、全ての要素が比較となる(全ての比較が終わるまで結果が出ない)。

オブジェクトに関する『同じ』

同等

オブジェクトが別のものであっても、対応する要素が一致した場合(==演算)。

同一

同じリファレンス(メモリ上の同じ位置)が指すオブジェクト同士である場合(is演算)。

キャッシュによる同一

短い文字列などは、効率化のためにキャッシュされる事がある。

S1 = 'hoge'
S2 = 'hoge'
S1 is S2

上記のis演算はTrueを返す。

ブール

  • 数値は0でなければTrue
  • その他のオブジェクトは空でなければTrue

空を意味するプレースホルダ

Noneという特殊なオブジェクトは、「オブジェクトが存在しない」事を意味する。

代入ステートメント

アンパック代入

[a, b] = ['foo', 'bar']
(x, y) = (10, 20)

上記のようにリストやタプルを利用して複数の変数に一度に代入を行う事が出来る。左右の型(リスとかタプルか)が異なっていても問題は無く、代入先と値とが対応していれば良い。

マルチターゲット代入

a = b = 'c'

同じオブジェクトへのリファレンスを同時に複数の変数に代入する事が出来る。

rangeを利用した列挙

アンパック代入から派生した、以下のような代入操作で列挙を実現出来る。

>>> a, b, c = range(3)
>>> a, c
(0, 2)
>>> range(3)
[0, 1, 2]

変数名

  • 先頭は英文字かアンダースコア、その後は英数字かアンダースコア
  • 大小文字は区別される
  • 予約語は利用不可能

慣例

  • 先頭がアンダースコア1つで始まる名前は通常使わない(from module import *ステートメントでインポート出来ない)
  • 先頭と末尾にアンダースコアを2つ重ねた名前は使わない(システム定義の特別な意味を持つ名前に使われる事が多い)
  • 先頭だけアンダースコアを2つ重ねた名前は通常使わない(所属するクラスの名前をつけた名前に変換される; マングリング)
  • アンダースコア1つだけの名前は使わない(対話型コマンドラインで直前に実行された演算結果を保持するために使われる)

拡張代入ステートメント

二項演算子は全て拡張代入ステートメントで利用可能。

X += Y
X &= Y
X -= Y
X |= Y
X *= Y
X ^= Y
X /= Y
X >>= Y
X %= Y
X <<= Y
X **= Y
X //= Y
  • オブジェクトの評価が1度で済む
    • オブジェクトが式に登場する回数が減るため、評価の回数も減る
  • 速度の面で有利な操作方法が自動的に選択される
    • 可変性オブジェクトの場合、コピーでなく上書きが行われる

printステートメント

標準出力にエコーするステートメント。デフォルトでは、カンマ区切りの変数をスペースで連結して末尾を改行とする。

>>> a = 'foo'
>>> b = 'bar'
>>> print a, b
foo bar
>>>

末尾に改行を入れない場合は、変数指定の末尾をカンマで終えれば良い。また、文字列フォーマットを利用すれば指定通りのフォーマットで文字列を作る事が出来る。

出力ストリームのリダイレクト

printステートメントの出力先を標準出力ストリーム以外に対応づける事も出来る。

import sys
sys.stdout = open('output_file', 'a')
...
print a, b, c

printステートメントの出力先となるsys.stdoutにファイルを関連づける事で、printステートメントを利用してファイルへの出力が可能(対応づけられるオブジェクトは、一定のプロトコルに従うオブジェクトであればファイルに限らない)。

>>を利用したリダイレクト

>>を利用する事でsys.stdoutを変更する事無く、直接オブジェクトへ出力する事が出来る。

log = open('log', 'a')
print >> log, a, b, c # ファイルオブジェクトへの出力
print x, y, z         # 標準出力への出力

ifステートメント

構文

if <cond1>:
    <statement1>
elif <cond2>:
    <statement2>
else:
    <statement3>

多分岐

Pythonではswitch-caseの様な多分岐のためだけに用意されたステートメントは存在しない。if-elif-elseを利用。

ディクショナリを利用した多分岐

>>> choise = 'three'
>>> print {'one': 1,
...        'two': 2,
...        'three': 3}[choise]
3

ディクショナリに値として関数を組み込んで、ジャンプテーブルを作る方法もある。lambda演算子を利用して無名関数を利用する事も出来る。

ブロックの区切り

Pythonではブロックをインデントで判別する。インデントはスペース文字でもタブ文字でも良く、その数に決まりは無い。

ステートメントの区切り

原則として、1行でステートメントは区切られる。

  • ステートメント中の(), {}, []といった括弧で囲まれた部分は拭く数行に渡って記述する事が出来る
  • バックスラッシュを行末に付けることで複数行に渡って記述する事が出来る
  • 文字列リテラルをトリプルクォーテーションを使って拭く数行で記述する事が出来る

ショートサーキット評価

A or B or C or ...

上記のようなorによるブール演算は、Trueが評価された時点で評価を終了する。

A and B and C and ...

同様に、andによるブール演算では、Falseが評価された時点で評価を終了する。

whileステートメント

while <cond>:
    <statement1>
else:
    <statement2>

elseブロックをネストした場合、このブロック部分はループが終了する際に実行される。

break, continue, passステートメント

  • break
    • 所属するループを無条件で抜ける(elseブロックも実行しない)
  • continue
    • ループの先頭に無条件で戻る
  • pass
    • 何もしない

ステートメントを式として利用出来ない

Pythonでは代入式は値を返さないため、以下のようなC的な記述は出来ない

while ((x = get_hoge()) != NULL) {...}

forステートメント

for <target> in <object>:
    <statement1>
else:
    <statement2>

シーケンスオブジェクトと要素が代入されるターゲット変数を指定。while同様、elseブロックを記述する事が出来る。ターゲットはタプルなどのアンパック代入も可。

rangeを利用した文字列シーケンス操作

>>> S = 'abcde'
>>> for i in range(0, len(S), 2):
...    print S[i],
...
a c e

zip関数とdictでディクショナリ生成

zip関数を利用すると、複数ののシーケンスをアンパックした新しいシーケンスを作成出来る。

>>> a = (1,2,3)
>>> b = ('a', 'b', 'c')
>>> zip(a, b)
[(1, 'a'), (2, 'b'), (3, 'c')]

ディクショナリオブジェクトのコンストラクタdictの引数にzip関数を利用すると、以下のような結果を得る事が出来る。

>>> dict(zip(a,b))
{1: 'a', 2: 'b', 3: 'c'}

また、zip関数をforステートメントのシーケンスとすれば、複数シーケンスに同時アクセスする事が出来る。

>>> for (x,y) in zip([1,2,3],[4,5,6]):
...     print x,y
... 
1 4
2 5
3 6

関数

defステートメント

Pythonではdefステートメントによって、関数オブジェクトが作成される。

def <name>(arg1, ...):
  <statement>

実行時にdefステートメントが処理されてから関数オブジェクトが作成されるため、当然それ以前は関数(オブジェクト)を利用する事は出来ない。

関数オブジェクト

defステートメントによって関数オブジェクトが作成され、そのリファレンスが関数名に代入される。

ネストされたdefステートメント

defステートメントは通常のステートメント同様、ネストされた位置に記述する事が出来る。

if cond:
    def func1():
        ...
else:
    def func1():
        ...

スコープ

グローバルスコープ

モジュールファイルのトップレベルで作成された変数はグローバルスコープに属する。『グローバル』は『モジュールファイルについてグローバル』である事に注意。あるモジュールファイルに属する変数は、そのモジュールファイル内でのみグローバルであり、他のモジュールファイルでは有効でない。

スコープの階層

  1. ビルトインスコープ
  2. グローバルスコープ
  3. 外側の関数のスコープ(ネストスコープ)
  4. ローカルスコープ
ビルトインスコープ

__builtin__モジュールのスコープ。

グローバルスコープ

モジュールのトップレベルのスコープ。

外側の関数のスコープ(ネストスコープ)

関数がネストされた場合の、一番最内側にネストされた関数以外が関するスコープ。

ローカルスコープ

関数が所属するスコープ。ネストされている場合、一番内側にネストされた関数のみが所属する。

変数の検索

変数は自動的にスコープを超えて検索される。ローカルスコープでそのローカルスコープ内では未定義の変数を利用しようとした場合、スコープの階層を順に上にたどりながら検索がなされ、見つかった時点で検索は終了する。

スコープを超える参照と代入

所属するスコープ外の変数を参照する場合、自動的に検索がなされるため、特別なステートメントは不要。一方、代入に付いては、globalステートメントを利用して明示的に宣言しない限り、所属するスコープ内の変数であるとされる。

ネストスコープを利用したクロージャ

ネストスコープを利用して関数を作成する事で、ネストスコープの変数をその関数の処理が終了してスコープが存在しなくなっても保持し続ける事が出来る。

>>> def f1():
...   x = 100
...   def f2():
...     print x
...   return f2
... 
>>> action = f1() # ここでf1は終了
>>> action()
100

既に存在しない外側のスコープに属する変数を保持しているオブジェクトの事をクロージャと呼ぶ事もある。

引数

引数が渡される際には代入が行われる。Pythonの代入はリファレンスの代入であるため、ローカルスコープで引数として受け取った変数からオブジェクトを上書きすれば外側の変数も影響を受ける。

引数のパターン

指定位置によるマッチング

defステートメントによる引数の並びと、関数呼び出し時の引数の並びによって引数のマッチングを行う。

def func(a, b, c):
  return a, b, c
func(1, 2, 3)
変数名によるマッチング

defステートメントによる引数名と関数呼び出し時に指定する引数名によって引数のマッチングを行う。この引数をキーワード引数と呼ぶ。

def func(a, b, c):
  return a, b, c
func(c=1, b=2, a=3)
デフォルト値

defステートメントで引数にデフォルト値を指定しておくと、関数呼び出し時に引数が省略された際に利用される。

def func(a, b, c=3):
  return a, b, c
func(1, 2)
可変個引数(タプル)

defステートメントで引数名の頭に*を一つ付ける事で、関数呼び出し時の引数を可変個にする事が出来る。この場合、関数の引数はタプルに収められる。

def func(*args)
  print args
print func(1)
print func(1, 2, 3)
可変個引数(ディクショナリ)

defステートメントで引数名の頭に*を二つ付ける事で、関数呼び出し時の引数を可変個にする事が出来る。この場合、関数の引数はディクショナリに収められる。ただし、関数呼び出し時の引数はキーワード引数である必要がある。

def func(**args)
  print args
print func(a=1)
print func(a=1, b=2)

引数を指定する際の注意

1つの関数で複数種の指定方法を利用する場合、以下に注意する。

  • 関数呼び出しの際、キーワード引数の後に通常の引数を指定しない
  • defステートメントによる見出し行では、*を利用した引数の後に通常の引数を指定しない
    • また、**を利用した引数は必ず最後に指定する

関数呼び出し時には、内部で以下の順にマッチングが行われる。

  1. 通常の位置によるマッチング
  2. キーワード引数の名前によるマッチング
  3. *を利用した引数があれば、これまでにマッチング出来なかったキーワード引数以外をタプルにまとめる
  4. **を利用した引数があれば、これまでにマッチング出来なかったキーワード引数をディクショナリにまとめる
  5. 指定されなかった引数に付いてデフォルト値が設定されていればその値を代入する

lambda式

lambdaキーワードによるlambda式を使って、関数を作成する事が出来る。

lambda 引数1, 引数2, ..., 引数N: 式
  • lambda式は『式』であるため、ステートメントであるdefステートメントが(文法的に)使用出来ない場所でも使用可能
    • 関数名に自動的に関数オブジェクトが代入されるdefステートメントと異なり、lambda式は式の戻り値が関数オブジェクトとなるため、明示的に変数に代入しない限り無名である
  • lambda式のボディも式である
    • defステートメントと異なり、評価された式の結果が自動的に返る
  • defステートメント同様に、ネストスコープのルールが適用される
  • コールバック関数として利用される
  • ジャンプテーブル(行うべき処理を要素とするリストやディクショナリ)を作成する際に利用される

apply関数

apply関数を利用してプログラム実行時に関数の名前や引数を決定する事が出来る。

action = <関数オブジェクト>
args   = <引数のタプル>
...
apply(action, args)

また、関数呼び出し時に引数の頭に*や**を付ける事で、同様の操作が実現出来る。

>>> def func(a, b, x, y):
...   return (a + b) * x + y
... 
>>> targs = (1, 2)
>>> dargs = {'x': 100, 'y': 200}
>>> func(*targs, **dargs)
500

map関数

「シーケンスオブジェクトの各要素に付いて同じ処理をする」という際に利用出来る。

>>> def func(x): return x * 10
... 
>>> seq = [1, 2, 3, 4, 5]
>>> map(func, seq)
[10, 20, 30, 40, 50]

map関数による実行は、一般にforステートメントによりも実行速度が速い。

以下のように、複数のシーケンスを与えた場合、それぞれの要素についての結果をリストで得る事も出来る。

>>> map(pow, [2,4], [2,2])
[4, 16]

ほか、シーケンスについて関数を適用する関数

filter関数

シーケンスの要素から特定の条件にあう要素群を返す。

>>> filter((lambda x: (x % 2) > 0), range(1,10))
[1, 3, 5, 7, 9]

reduce関数

はじめに要素の1番目と2番目に対して関数を実行し、以降は直前に実行された関数の結果とその次の要素について関数を実行していく。

>>> reduce((lambda x, y: x + y), range(1,10))
45

リスト内包表記

式をシーケンスの各要素に適用させる。

forループで代入されるシーケンス要素x全てについて式を実行する。

>>> result = [x * 100 for x in range(1, 5)]
>>> result
[100, 200, 300, 400]

forループで代入されるシーケンス要素xのうち、ifでの条件判定がtrueである場合ついて式を実行する。

>>> [x for x in range(1,10) if x % 2 == 1]
[1, 3, 5, 7, 9]

ジェネレータとイテレータ

通常、関数ではreturnによって値を返し処理を終了させる。yieldステートメントを利用した場合、値を返して処理を中断し、その状態を保持しておき再開させる事が出来る。

yieldステートメントを含む関数が呼び出された時には、イテレータプロトコルをサポートするジェネレータオブジェクトが返される。イテレータプロトコルをサポートするオブジェクトは、nextという次の要素を返すメソッドを持つ。

forループを利用して暗黙的に最後の要素を取得するまでジェネレータオブジェクトのnextを呼び出す。

>>> def gen(n):
...   for i in range(n):
...     yield i ** 2
... 
>>> for i in gen(5):
...   print i, '/',
... 
0 / 1 / 4 / 9 / 16 /

明示的にnextメソッドを呼び出す。要素が無くなった場合、StopIteration例外。

>>> x = gen(5)
>>> x.next()
0
>>> x.next()
1
>>> x.next()
4
>>> x.next()
9
>>> x.next()
16
>>> x.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

iter関数

ビルトインオブジェクトは、iter関数を利用する事で対応するイテレータオブジェクトを作成する事が出来る。forループのシーケンスオブジェクトの指定部分には、暗黙的にiter関数が呼ばれている。

ジェネレータ式

リスト内包表記に似た構文を利用して、ジェネレータを式で記述する事が出来る。

  • リスト内包表記は角括弧だが、ジェネレータ式は丸括弧
>>> g = (x**2 for x in range(10))
>>> g.next()
0
>>> g.next()
1
>>> g.next()
4
>>> g.next()
9
>>> g.next()
16
>>> g.next()
25
>>> g.next()
36
>>> g.next()
49
>>> g.next()
64
>>> g.next()
81
>>> g.next()

モジュール

  • importステートメント
    • モジュール全体をインポートする
  • fromステートメント
    • モジュールに属する特定の名前だけをインポートする
  • reload関数
    • モジュールをリロードするための関数
      • セッションを停止する事無くモジュールのコードを再起動する
import something
something.foo('bar')

インポートの処理

  1. 対象のモジュールファイルを探す
  2. 見つかったモジュールファイルをコンパイルし、バイトコードにする
  3. モジュールのコードを実行し、モジュール内で定義されているオブジェクトを作成する

モジュールを探す

importステートメントで指定されたモジュールは、モジュールサーチパスから自動的に検索される。

モジュールサーチパス
  1. トップレベルファイルのホームディレクトリ
  2. 環境変数PYTHONPATH(設定されている場合)
  3. 標準ライブラリモジュールのディレクトリ
  4. *.pthファイルの内容(存在する場合)
*.pthファイル

1行に1つずつディレクトリを指定する

現在の設定を知る

sys.pathを利用して、現在設定されているモジュールサーチパスをリストで取得可能。

import sys
print sys.path
インポート対象のファイル
  • ソースファイル(*.py)
  • バイトコードファイル(*.pyc)
  • パッケージインポートのためのディレクトリ
  • Cエクステンションモジュール(*.so)
  • フローズンバイナリに対応するメモリイメージ
  • Jythonシステムの場合にはJavaクラス
  • zipファイルのコンポーネント(zipimportモジュールを利用した場合にインポート可能)

コンパイルする

ソースファイル(*.py)のタイムスタンプがバイトコードファイル(*.pyc)より新しい場合、未コンパイルとして扱いバイトコードにコンパイルする。

  • バイトコードファイルはインポートに際して作成される
    • トップレベルファイルについてバイトコードファイルが作られる事は無い

fromステートメント

fromステートメントを利用すると、モジュールに属する名前の変数をコピーする事が出来る。つまり、モジュールオブジェクトの名前を指定する必要がなくなる。

from something import hoge
hoge('hello')

from *ステートメントを利用すると、モジュール内のトップレベルに属する全ての変数をコピーする事が出来る。

from something import *
hoge('hello')

インポートは一度だけ実行される

コード内で、特定のモジュールを複数回インポートしても、実行されるのは最初の1回だけ。

importステートメント、fromステートメントでは代入が行われる

importもfromも、defと同様にステートメントであり、インポートが行われるのはコンパイル時ではなくプログラム実行時である事に注意する。暗黙的な(名前(変数)への)代入やネスト可能である点など、defと共通する点が多い。

  • importでは、モジュールオブジェクト(モジュール全体)が1つの変数(モジュール名)に代入される
  • fromでは、指定されたモジュール中の変数が、インポート先のモジュールのスコープに属する同名の変数に代入される

モジュールのリロード

reload関数を利用してモジュールをリロード(再読み込み、再実行)する事ができる。

前回のロード・実行以降にモジュールファイルが変更されていた場合、その変更がモジュールオブジェクトに変更される。この特性を利用して、ダイナミックなデバッグ作業が簡単に行える(プログラムの再起動なしにデバッグ作業を進められる)。

reload関数

reload関数には、モジュール名ではなく、モジュールオブジェクトの名前(変数)を指定する。

  • リロードの際、モジュールの名前空間は必要に応じて変更が加えられる(名前空間が破棄されて作り直されるわけではない)
  • リロード時に代入が行われると、変数は新しいものに置き換えられる
  • リロードを行うと、そのモジュールをimportステートメントでインポートしている全てのモジュールに影響を与える
  • fromステートメントによってモジュールをインポートした後にリロードが行われても影響を受けない
    • リロード後にfromステートメントを利用した場合は、リロードの影響を受ける
  • リロードの対象は、引数に与えられたモジュールのみ
    • 引数に与えられたAというモジュールからBモジュールとCモジュールをインポートしていてもリロード対象はAのみ

パッケージ

インポートはファイルだけでなくディレクトリに付いても実行可能。ディレクトリパスを指定して行うインポートをパッケージインポートと呼ぶ。

パッケージインポート

import dir1.dir2.module # dir1/dir2/module.pyをインポート

__init__.pyファイル

パッケージインポートの際には、指定するディレクトリパス内に__init__.pyというファイルが必要。__init__.pyファイルが存在しさえすれば、そのディレクトリはパッケージとして扱う事が出来る。

__init__.pyファイルは、パッケージインポートの際に最初に自動的に実行されるため、初期化処理などに利用される。

モジュールに関連するテクニック

データの隠蔽

Pythonではモジュール中の特定のデータを外部から隠蔽する事は出来ない。

下線と__all__属性

擬似的な隠蔽手段として、以下を利用する事が出来る。

  • _xなどとした場合、from *ステートメントによるコピー対象から除外される
  • __all__属性を利用して、from *ステートメントによるコピー対象から除外する

拡張機能

拡張機能を利用する場合、以下のように__future__を利用してインポートを行う。

from __future__ import 機能名

モジュールに別名を付ける

import longname as shortname
from module import longname as shoftname

クラス

classステートメントを利用してクラスオブジェクトを作成する事が出来る。

クラスオブジェクトの作成
class C1:
    ....
class C2:
    ....
継承
class C3(C2):
    ...
多重継承
class C3(C1, C2):
    ...
インスタンスオブジェクトの作成
I1 = C1()

多重継承

クラスオブジェクトを作成する際、スーパークラスへのリンクを複数指定する事が出来る。この時の並びは意味を持ち、継承すべき属性は左から順に検索される。検索は属性が見つかった時点で終了される。

属性をオブジェクトツリーから検索する順序は、下から上、左から右となり、下から上への検索は左から右への検索より優先される。

クラスメソッド

通常の関数と同様に、defステートメントを利用してオブジェクトを作成する。

>>> class C1:
...     def func(self, arg):
...         self.name = arg
... 
>>> I1 = C1()
>>> I1.func('hoge')
>>> print I1.name
hoge

クラスメソッドの第1引数には自動的にインスタンス自身を示すself変数がセットされる。

コンストラクタ

インスタンスオブジェクトが作成する時に自動的に実行される__init__メソッドをオーバーライドする事で、コンストラクタを実現する事が出来る。

クラスの属性

classステートメントの内側で代入が行われた変数は、クラスの属性となる。

インスタンスの属性

クラスメソッドの第1引数(慣例ではself)を利用する事で、インスタンスオブジェクトの属性を作成する事が出来る。

演算子のオーバーロード

__X__のような名前の前後にアンダースコアが2つ重なったメソッドを利用して、演算子のオーバーロードを行う事が出来る。

抽象クラス

assertステートメントを利用して、抽象メソッドが呼び出された場合に例外を発生させるテクニックがある。

フックメソッド

名前の前後にアンダースコアが2つ付いたメソッドをフックメソッドと呼ぶ。フックメソッドは演算子のオーバーロードに用いられる。

__init__

インスタンスの作成

__del__

ガーベージコレクション

__add__

+演算子

__or__

|演算子

__repr__

出力(print関数)

__str__

型変換(str関数)

__call__

関数の呼び出し

__getattr__

.(ピリオド)を使用した属性へのアクセス

__setattr__

属性への値の代入

__getitem__

インデクシング(I1[index] = value)、forループ、X in Y

__len__

シーケンスの長さの確認(len関数)

__cmp__

比較一般

__lt__

小なり演算(<)

__eq__

同等かの比較(==)

__radd__

インスタンスが+演算子の右側に使われた場合

__iadd__

上書き演算

__iter__

ループ、反復

ループの場合

ループ時には、__iter__メソッドが呼び出され、IndexError例外が発生した時に、__getitem__メソッドが呼び出される。

__getattr__

存在する属性が検索された場合には呼び出されない。

__setattr__

__setattr__メソッド内でインスタンスの属性に代入を行った場合、再度__setattr__メソッドが呼び出されてしまい、無限ループとなりスタックオーバーフロー例外が発生する。__setattr__メソッド内での代入は、__dict__に対してインデクシングを行う(__dict__は属性を要素とするディクショナリ)。

__repr__と__str__

インスタンスがprintやstrなどの対象となった時に、文字列変換するために呼び出される。まず、__str__が探され、__str__が無い場合に__repr__が探される。

__str__は人が見易い形に変換し、__repr__は元のオブジェクトに戻せる形式に変換するようにコードを書く。

__radd__

インスタンスが+演算子の右側に使われた場合、__add__メソッドは対応出来ないため、__radd__メソッドを利用する。ただし、+演算子の左側にインスタンスが置かれた場合には__add__メソッドが利用される。

__call__

インスタンスオブジェクトが関数呼び出しとして利用された場合に実行されるメソッド。

インスタンスオブジェクトをコールバックハンドラとして使用するテクニックに利用される。

名前空間の実体はディクショナリ

モジュールもクラスも、その名前空間の実体はディクショナリであり、__dict__というビルトイン属性を利用してアクセスする事が出来る。

インスタンスが作成された時点では、その名前空間に対応するディクショナリは空。ただし、クラスの名前空間に対応するディクショナリにリンクされるため、クラスの名前空間も検索対象とする事が出来る。

インスタンスは、__class__という属性を持ち、これによってどのクラスにリンクされているかが分かる。

クラスは、__bases__という属性を持ち、これによってクラスがどのようなスーパークラスにリンクされているかが分かる。

デリゲーション(委譲)

属性へのリクエストを集中させる目的で、__getattr__メソッドが利用される。__getattr__メソッド内でビルトイン関数のgetattr関数を呼び出す事で、移譲先オブジェクトの属性にコントローラオブジェクトの属性のようにアクセスする事が出来る。

class Delegator:
    def __init__(self, object):
        self.object = object

    def __getattr__(self, attrname):
        return getattr(self.object, attrname)

D = Delegator([1,2,3])
D.append(100)
print D

getattr関数の場合、__dict__と異なり、上への検索が実行される。

ミックスイン

あるスーパークラスを継承して専門的な処理を行うクラスを定義する際に、ある特別なインターフェースと付随する機能が必要になった時に、そのインターフェースを提供する汎用的なスーパークラスも同時に継承する事が出来る(多重継承)。

ファクトリ

apply関数や可変個引数を利用して、ファクトリを実現する事が出来る。

def factory(callable, *targs, **pargs):
    return apply(callable, targs, pargs)

class C1:
    def __init__(self, param):
        self.param = param
    def something(self):
        print self.param

class C2:
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar

obj1 = factory(C1, 'print this')
obj2 = factory(C2, 'this is foo', 'this is bar')

obj1.something()
print obj2.foo, obj2.bar

結合メソッドと非結合メソッド

  • 結合メソッド
    • クラス名を利用してメソッドを呼び出した場合
  • 非結合メソッド
    • インスタンスを利用してメソッドを呼び出した場合

結合メソッドを呼び出す際には、第1引数に明示的にインスタンスオブジェクトを指定する必要がある。

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

ドキュメンテーション文字列は、代入を行わない文字列オブジェクトをコード中に記述したもの。ドキュメンテーション文字列は、モジュールファイル、関数のdefステートメント、クラス、メソッドのコード。

mydoc.py
"this is module document."

class C1:
    "this is class document."

    def method(self, arg):
        "this is method document."
        pass

def func(arg):
    "this is function document."
    pass
抽出結果
>>> import mydoc
>>> mydoc.__doc__
'this is module document.'
>>> mydoc.C1.__doc__
'this is class document.'
>>> mydoc.C1.method.__doc__
'this is method document.'
>>> mydoc.func.__doc__
'this is function document.'

ビルトインクラスの機能拡張

Python2.2から、旧来は型変換のための関数名であったlist,str,dict,tupleなどが、ビルトインオブジェクトの型を表すオブジェクト(ビルトイン名)になった。また、型変換を実行するlist('...')などの関数はコンストラクタを呼び出すためのコードに変わっている。

これらの変更により、ビルトイン名を利用してビルトインオブジェクトのサブクラスを作成する事が出来るようになった。

ネームマングリング

変数をその変数が作られたクラス固有のものにする機能として、マングリング機能が利用出来る。

classステートメント内の変数名の頭にアンダースコア2つ付けた場合、自動的にアンダースコア1つとクラス名が拭かされる(class C1:内の__xという変数の場合、_C1__x)。

新スタイルクラス

Python2.2以降、新しい種類のクラスが利用出来る。ビルトインクラスのサブクラスを作成する時に自動的に新スタイルクラスになる。

新スタイルクラスでは、オブジェクトツリーの検索優先度が、下から上よりも左から右が優先される。

スタティックメソッドとクラスメソッド

クラスメソッドの第1引数にインスタンスオブジェクトを渡さずに使用可能なメソッド。staticmethodやclassmethodというビルトイン関数を利用する。

__slots__属性

クラスのインスタンスオブジェクトに持たせる事が出来る属性の名前を限定する事が出来る。

>>> class slots(object):
...     __slots__ = ['hoge', 'foo']
... 
>>> x = slots()
>>> x.hoge = 'HOGE'
>>> x.foo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: foo
>>> x.foo = 'FOO'
>>> x.bar = 'BAR'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'slots' object has no attribute 'bar'

__slots__属性の代入時点では変数は作成されない(インスタンス作成後に明示的に代入する必要がある)。

__dict__属性を持てなくなるなどの制約がある。

プロパティ

フックメソッドのようにメソッドが自動的に呼び出されるような仕組みを作る事が出来る。プロパティの場合、あらかじめ設定された属性に関してのみ呼び出しが行われる。

attr = property(...)

propertyメソッドの引数には3つのメソッド(属性の値を取得するメソッド、属性の値を設定するメソッド、属性を削除するメソッド)とドキュメンテーション文字列を指定する。メソッドの指定には、対応する機能を無効にする意味でNoneオブジェクトを指定する事が出来る。

>>> class Prop(object):
...     def get(self):
...         return 1
...     hoge = property(get, None, None, None)
... 
>>> x = Prop()
>>> x.hoge
1
>>> x.foo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Prop' object has no attribute 'foo'
>>> x.hoge = 100
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

__getattribute__メソッド

存在しない属性のみが対象となる__getattr__メソッドと異なり、属性へのアクセス全てに対応する。

例外

  • try/except
    • 特定の例外を検知して処理する
  • try/finally
    • 例外の発生によらず、何らかのクリーンアップ処理を行う
  • raise
    • 例外を明示的に発生させる
  • assert
    • ある条件が満たされた時に例外を発生させる

try/except

try:
    ...
except <オブジェクト>:
    ...
...

tryステートメント内で例外が発生した場合、例外を知らせるオブジェクトと対応するexceptステートメントのブロックにジャンプする。

try/finally

try:
    ...
finally:
    ...

例外が発生したか否かに関わらず、finallyブロックに書いたコードがtryステートメント中のコードが終了する前に必ず実行される。例外が発生した場合、直前の例外ハンドラに例外が渡るため、ステートメント後のコードから処理が継続されない点に注意。

try/except/else

try:

   ...

except <name1>:

   ...

except <name2>:

   ...

else:

   ...

tryステートメントでは、最後にelseブロックを記述する事で、例外が発生しなかった場合の処理を記述する事が出来る。

tryステートメントのブロック

  • except:
    • 全ての例外に対応する
  • except name:
    • nameに対応する例外のみ処理する
  • except name, value:
    • nameに対応する例外を処理し、valueに保存されたデータを受け取る
  • except (name1, name2):
    • 括弧内に列挙された例外全てを処理する
  • except (name1, name2), value:
    • 括弧内に列挙された例外全てを処理し、valueに保存されたデータを受け取る
  • else:
    • 例外が発生しなかった場合に実行される
  • finally:
    • 常に実行される

raiseステートメント

  • raise name
    • nameという名前の例外を発生させる
  • raise name, data
    • nameという名前の例外を発生さ、dataという名前のデータも渡す
  • raise
    • 直近に発生した例外を再度発生させる

assertステートメント

  • assert test, data
    • 条件testを満たさない場合に例外を発生させる
    • dataが指定されている場合、データとして渡す事が出来る

-Oフラグが使用されない限り、ビルトイン名__debug__には1(true)が設定されるため、コンパイルオブションによってプログラムの振る舞いを返る事が出来る(デバッグコード)。

文字列例外

ユーザ定義例外を利用する際に、その実体を文字列オブジェクトとして作成した場合、例外発生側と例外処理側とで同じ変数(リファレンス)を使用しなくてはならない。同等の文字列を利用すればよいというわけではない。

クラス例外

文字列例外の場合、例外のマッチングはisによって行われるが、クラス例外の場合、同じツリー上に属するクラスであるかを基準に行われる。

ビルトイン例外

ビルトイン例外はビルトイン名であり、また、標準ライブラリモジュールexceptionsの属性でもある。

関連ツール

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

ドキュメンテーション文字列を出力するためのツール。

PyChecker

実行前に誤りの確認が出来るツール。

PyUnit

UnitTest用のツール。

doctest

リグレッションテスト(退行テスト)に便利なツール。

  • 標準ライブラリモジュールdoctest

IDE

  • IDLE
  • Komodo

プロファイラ

  • timeモジュール
  • profileモジュール

デバッガ

  • pdb

プログラムの配布

  • Py2exe
  • Installer
  • freeze

実行速度の向上

  • Psyco
Last modified:2007/02/13 00:02:16
Keyword(s):[python]
References: