ラベル Python の投稿を表示しています。 すべての投稿を表示
ラベル Python の投稿を表示しています。 すべての投稿を表示

2010年11月3日水曜日

[Python, Solaris] pynvpairにnvlistのイテレータ追加

pynvpairにnvlist_next_nvpair()を使ったイテレータを追加したのでforでまわせるようになった。

nvlist_print()の代替実装例。

def nvlist_print(nvlist, indent = 0):
    for type, name, value in nvlist:
        if not isinstance(value, list):
            if type == DATA_TYPE_NVLIST:
                print "%s%s:" % (" " * indent, name)
                nvlist_print(value, indent + 4)
            else:
                print "%s%s: %s" % (" " * indent, name, value)
        else:
            print "%s%s:" % (" " * indent, name)
            indent2 = indent + 4
            for i, v in enumerate(value):
                if type == DATA_TYPE_NVLIST_ARRAY:
                    print "%s[%d]:" % (" " * indent2, i)
                    nvlist_print(v, indent2 + 4)
                else:
                    print "%s[%d]: %s" % (" " * indent2, i, v)

2010年11月1日月曜日

[Python, Solaris] libnvpairのpythonインタフェース

OpenSolaris動作するlibnvpairのpythonインタフェース。
b134で動作確認。

こんな感じ。
>>> from pynvpair import *
>>>
>>> a = nvlist()
>>> a.add(DATA_TYPE_UINT64_ARRAY, "test", (0, 100, 12345))
>>> a.dump()
test[0]: 0
test[1]: 100
test[2]: 12345
>>> a.lookup(DATA_TYPE_UINT64_ARRAY, "test")
[0L, 100L, 12345L]
>>> 
何かに使えそう。 

2010年10月24日日曜日

[Python] 例外処理とif...elseのコスト

例えば、あるループ中にあるシーケンスの要素を参照するとき。
for i in range(10):
    value = a[i]
これだと、インデックスiが有効かどうかわからないのでどうにかしたい。

(例1)
とりあえず長さをチェックして参照する。
for i in range(10):
    if i < len(a):
        value = a[i]
これはループで毎回len(a)するのでいかにも遅そう。

(例2)
シーケンスの長さが変わらないのであればループの前にあらかじめ長さをとっておこう。
alen = len(a)
for i in range(10):
    if i < alen:
        value = a[i]

(例3)
とりあえず参照して例外を拾おう。
for i in range(10):
    try:
        value = a[i]
    except IndexError:
        pass

どれを使うのがよさげなのか計ってみよう。

テスト用スクリプト
#!/usr/bin/env python
import timeit

# インデックスiが範囲内の場合のセットアップ
setup1 = """
a = [ 1, 2, 3 ]
alen = len(a)
i = 1
"""

# インデックスiが範囲外の場合のセットアップ
setup2 = """
a = [ 1, 2, 3 ]
alen = len(a)
i = 5
"""

# 例1用ステートメント
stmt1 = """
if i < len(a):
    a[i]
else:
    pass
"""

# 例2用ステートメント
stmt2 = """
if i < alen:
    a[i]
else:
    pass
"""

# 例3用ステートメント
stmt3 = """
try:
    a[i]
except IndexError:
    pass
"""

print timeit.Timer(setup = setup1, stmt = stmt1).timeit()
print timeit.Timer(setup = setup1, stmt = stmt2).timeit()
print timeit.Timer(setup = setup1, stmt = stmt3).timeit()
print timeit.Timer(setup = setup2, stmt = stmt1).timeit()
print timeit.Timer(setup = setup2, stmt = stmt2).timeit()
print timeit.Timer(setup = setup2, stmt = stmt3).timeit()
実行結果
# インデックスが範囲内
0.361014127731 # 例1
0.125293016434 # 例2
0.128052949905 # 例3
# インデックスが範囲外
0.284233808517 # 例1
0.101886034012 # 例2
1.82788801193  # 例3

例外処理で実装した場合(例3)のインデックスが範囲外になる時が飛びぬけて遅い。
つまり、例外発生のコストは結構大きい。
例外発生が頻繁に発生することが予測される場合、if...elseで簡単に
代替できるのであればif...elseを使ったほうが高速。

2010年10月21日木曜日

[Python] 実行時間計測 timeit

下例では変数aと変数bの値を入れ替える2つのやり方をそれぞれ計測している。

>>> import timeit
>>> start = """
... a = 100
... b = -100
... """
>>> stmt1 = """
... x = a
... a = b
... b = x
... """
>>> stmt2 = """
... a, b = b, a
... """
>>> timeit.Timer(setup = start, stmt = stmt1).timeit()
0.14281582832336426
>>> timeit.Timer(setup = start, stmt = stmt2).timeit()
0.10225486755371094

timeitモジュールのTimerクラスのインスタンスを作る。
引数setupは時間計測の最初に1回だけ実行するその名のとおりセットアップ用の文。
セットアップにかかる時間は計測時間に含まれない。
引数stmtは計測対象の文。
timeit()を呼び出すとデフォルトで100万回stmtを実行する時間を秒単位で返す。
timeit(100)のように回数を渡せる。

stmp2の方がstmp1に比べ40%高速だとわかる。