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を使ったほうが高速。

[expect] spawnしたプロセスの終了ステータスを得る

spawnしたプロセスの終了ステータスを得たいことがある。
この場合はwaitでプロセスの終了を待つと、結果が返ってくる。
結果はリストになっていて3番目(インデックス2)が-1の場合は
OSエラー、0の場合は4番目(インデックス3)にswawnしたプロセスの
終了ステータスが入っている。

スクリプトの例。読み取り権のないファイルをcatして終了ステータスを得る。
spawn cat /etc/shadow
expect eof
catch wait result
set OS_ERROR [ lindex $result 2 ]
if { $OS_ERROR == -1 } {
        puts "Fail to exec"
        exit 127
}
set STATUS [ lindex $result 3 ]
exit $STATUS
スクリプト自体をcatの終了ステータスで終了する。


実行結果
$ expect test.expect
spawn cat /etc/shadow
cat: /etc/shadow: Permission denied
$ echo $?
1
きちんと1が返っている。

expectはTcl言語の文法を知らないとちょっと戸惑う。
man expectでもよくわからないと思うことは実はTcl言語の文法だったり。

2010年10月21日木曜日

[Blogger] コードの表示

Bloggerできれいにコードの表示をしたいので探してみたらSyntaxHighlighterというのがあった。

デザインの「HTMLの編集」でテンプレートを編集する。

</head>の手前あたりに以下を追加。
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript">
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPython.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js' type='text/javascript'/>
<link href='http://alexgorbatchev.com/pub/sh/current/styles/shCore.css' rel='stylesheet' type='text/css'/>
<link href='http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css' rel='stylesheet' type='text/css'/>

</body>の手前あたりに以下を追加。
<script type='text/javascript'>
     SyntaxHighlighter.config.bloggerMode = true;
     SyntaxHighlighter.all()
</script>

これでテンプレートを保存。
コードの言語に応じたブラシが用意されているので必要なものをリンクしておけばよい。
上記例ではPython用のshBrushPython.jsとXML(HTML)用のshBrushXml.jsを記述している。


投稿では
<pre class="brush: python">
def func(a, b):
    pass
</pre>
とpreのclassにブラシを指定してやるだけ。

alexgorbatchev.comに感謝。

[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%高速だとわかる。