リストの中に空のリストを作る方法

結論から。

# n is number of lists
lists = [ [] for i in xrange(n) ]

最初、

# Bad pattern
lists = [[]] * n

のように生成しようとしていたが、これには致命的な問題があった。

n = 3

a = [ [] for i in xrange(n) ]
b = [[]] * n

# 一見、両者は全く同じに見える
print a
# [[], [], []]
print b
# [[], [], []]
a == b
# True

#だがしかし
a[0].append('hoge')
b[0].append('fuga')

print a
# [['hoge'], [], []]
print b
# [['fuga'], ['fuga'], ['fuga']] ← !?

そう、[[]]に掛け算をする方式だと、
生成された空のリストの参照が全て同じになってしまうのだ。

n = 3

a = [ [] for i in xrange(n) ]
b = [[]] * n

print map(hex,map(id, a))
# ['0x2546c48L', '0x23fa608L', '0x2547708L']

print map(hex,map(id, b))
# ['0x248ddc8L', '0x248ddc8L', '0x248ddc8L']

ということで、真夏の終わりの怪奇現象に悩まされた思い出として久々の投稿でした。

pexpectの挙動について

pexpect - Noah.org
pexpectとは、PythonLinux等のexpectコマンドみたいな機能が使えるべんりな野郎です。
インストールとかしなくても、pexpect.pyを入手して同じディレクトリに置けば使えちゃうので導入がめっちゃ手軽なのがポイント。
TelnetとかSSHとかFTPとかをPythonで扱いたい日でもコレで安心!

で、今日は日本語の情報が少なめなのと、挙動で悩んだ点があったので自分用メモがてら記録します。

import pexpect # 同じディレクトリに置いたらこれだけで使える。

c = pexpect.spawn("telnet 192.168.0.2")
# spawnした時点から、pexpectは入力に対する出力(=stdout?)の内容を内部的に保持する。

c.expect("login: ")
# expectした時点で、内部的に保持するstdoutをspawn.bufferに読み込み、
# 与えられた正規表現で検索を行い、結果を以下の変数に出力する。
# spawn.after - マッチした文字列
# spawn.before - マッチした文字列より前の文字列
# spawn.buffer - マッチした文字列より後の文字列

# つまり、上の例で言えば結果は
print c.after # "login: "
print c.before # "Trying 192.168.0.2...\r\nConnected to 192.168.0.2.\r\nEscape character is '^}'.\r\n"
print c.buffer # ""
# となる。

c.sendline("spam")
c.expect("Password: ")
c.sendline("hamegg")
c.expect("spam@blackknight:\/export\/home\/spam\([0-9]+\)> $")

# ログインも終わった所でexpectとbufferと内部保持するbufferの挙動について記す。

c.sendline("echo 'LOVELY SPAAAAAAAAAAAAM!'")

# まず、expectをすると、
c.expect("SPA+M!")
# spawn.bufferの中から、expectで求められた文字列を探す。

# さて、2つ前のexpectではログイン後のプロンプトにマッチしていた為、
# 当然ながらechoをsendlineした時点でのc.bufferの中身は "" である。
# expectされた文字列がbufferに見つけらないと、
# pexpectは内部保持する文字列をmaxsizeの文字数だけ仮bufferに書き込む。
# 仮buffer = "echo 'LOVELY SPAAAAAAAAAAAAM!'\r\nLOVELY SPAAAAAAAAAAAAM!\r\nspam@blackknight:/export/home/spam(101)> "
# 書き込まれた仮bufferを与えられた正規表現を元に先頭から検索する。
# 例で言えば、ヒットするのは1回目のSPAAAAAAAAAAAAM!である。
# つまり、上のexpectの結果、最終的にafter before bufferの中身は以下の通りとなる。

print c.after # "SPAAAAAAAAAAAAM!"
print c.before # "echo 'LOVELY "
print c.buffer # "'\r\nLOVELY SPAAAAAAAAAAAAM!\r\nspam@blackknight:/export/home/spam(101)> "

# 恐らく、期待する挙動と違ったのでは無いだろうか。
# 期待する挙動は、echoコマンドの出力結果に対してのヒットであり、
# echoコマンドの引数にヒットではなかったはずだ。

# これでは、echoコマンドが正確に帰ってきたかどうかのテストにはならない。
# そんな時は、spawn.readline()を使うと良い。

c.expect(".*$") # 文末までの全てにマッチ == bufferをクリア
c.sendline("echo 'LOVELY SPAAAAAAAAAAAAM!'")
c.readline()

# spawn.readline()は、spawn.expect("\r\n")と等価(のはず)である。
# つまり、コマンド入力行の末尾にマッチするので、

print c.after # "\r\n"
print c.before # "echo 'LOVELY SPAAAAAAAAAAAAM!'"
print c.buffer # "LOVELY SPAAAAAAAAAAAAM!\r\nspam@blackknight:/export/home/spam(102)> "

# このようになる。
# こうなったら、心置きなくexpectすればよい。

c.expect("SPA+M!")

print c.after # "SPAAAAAAAAAAAAM!"
print c.before # "LOVELY "
print c.buffer # "\r\nspam@blackknight:/export/home/spam(102)> "

# pexpectを使う上では、このbufferの管理が非常に大事なようで、コイツを見失うと、厄介な事になりかねない。
# cdした後に、CWDとファイルの存在を確認した上で、rm -rf ./* 等の簡易スクリプトを組んだ際、
# expectが発行したコマンドの引数にヒットしていて、実は・・・なんてことが起これば悪夢である。

c.sendline("exit")
c.expect(pexpect.EOF)
c.close()

では、今日はこのへんで。

Hornet

Hornet 2012/12〜
サブノート。最新ゲームが動く、持ち運べるゲームマシン。

CPU: Intel Core i7 3630QM (2.40GHz 4cores)
Memory: SanMax SMD-N16G28NP-16K-D (DDR3-1600 8GB x2) = 16GB
GPU: NVIDIA GeForce GTX670MX 3GB GDDR5/192bit
OS: Microsoft Windows 7 Home Premium 64bit
SSD: 128GB (mSATA)
HDD: 500GB (SATA2-5400rpm)
メーカー: G-Tune(マウスコンピュータ)

TiarraのLog::Channelの保存先ディレクトリをサーバ別にする

Perlなんて読んだことも書いた事もねーよバーカバーカ!

最近ようやくTiarraを使い始めたことにより、僕のIRCライフはハッピーそのものになった訳ですが、自動的にログを取ってくれるLog::Channelの設定項目にいささか不満が。
その不満とは、ログを保存するディレクトリ名が#チャンネル名@サーバ名固定で、それを分割することができないということ。

理想としては
channel: #(server)/#(chname) *
(実際に#(server)と#(chname)はない)
として、

--Log
 |--IRCNET
 | |--#hogehoge
 |  --#foobar
  --Friend
   |--#spam
   |--#ham
   |--#egg
    --#bean

のようにしたいのに、

実際は

--Log
 |--#hogehoge@IRCNET
 |--#foobar@IRCNET
 |--#spam@Friend
 |--#ham@Friend
 |--#egg@Friend
  --#bean@Friend

のようにしかならないんですね。

いや、確かに設定ファイルをチャンネルごとに記述したり、channel: IRCNET/#(channel) *@IRCNET のように記述すればいいっちゃいいんですが、前者はめんどくさいし後者はディレクトリ名にゴミが残るのがなんとも気に入らない。
ということで#(server)と#(chname)が使えるように書き換えてみよう。

tiarra/module/Log/Channel.pm 196行目から

            my @d_name = split(/@/, $chan_filename);
            my $chan_dir = Tools::HashTools::replace_recursive(
                $ch->[0], [{channel => $chan_filename,
                            lc_channel => lc $chan_filename,
                            chname => $d_name[0],
                            server => $d_name[1]}]);

ワーイ簡単だった
あれ、でもこれ設定ファイルのchannel-network-separatorをデフォから変更してる人はダメじゃね?


Perlよくわからないので有志の人がんばって!
(´・_c・) (・c_・`)
  〜 おわり 〜 

Pyazo 進捗状況 4/6

Pyazo/alpha/Pyazo.py at master · purintai/Pyazo
Alpha版
必要環境

実装状況

  • exeを叩いて起動させる。
    • 単体exeとして動かす為には、書いたコードをpy2exeなどでパッケージ化する。
      • 対応出来ず。py2exeがモジュールの依存関係を正しく読んでくれなかったり様々なトラブル(from 〜 import 〜 を正しく解析できない?)に見舞われた為、停止中。求ム助ケ
  • 画面上の領域をマウスでドラッグして選択する。
    • 画面上の領域をマウスで選択ということで、GUIをサポートする機能が必要。wxPythonなどを使用する。
      • 実装済wxPythonで半透明のウィンドウを表示し、領域を選択させる。
  • 選択した領域のスクリーンショットを撮影する。
    • 画像処理をサポートする機能が必要。PILなどを使用する。
      • 実装済。だが、マルチディスプレイに非対応。PILではなくPyGTK等を使えばマルチディスプレイに対応できるらしい。
  • アップロードされた画像の公開用URLをブラウザで開く。
    • Dropbox APIで公開用URLを取得後、webbrowserライブラリなどで対応。

と、そんな具合です。
ソースを公開してますがAPIキーは僕しか使えないので、使ってみたい方はAPIキーをご自分で取得して書き換えて使ってみて下さい。


ついでにDropbox使ってない人は←のURLから登録してくれると、それはとっても嬉しいなって

Pyazo - PythonでGyazoもどきを作ってみよう (2) 設計モドキ

とりあえず動かしたいものを定義してみよう。

exeなりショートカットなり叩いて起動させる。
 ↓
画面上の領域をマウスでドラッグして選択する。
 ↓
選択した領域のスクリーンショットを撮影する。
 ↓
撮ったスクリーンショットDropboxにアップロードする。
 ↓
アップロードされた画像の公開用URLをブラウザで開く。

という極めてシンプルな流れ。
では、各動作ごとの実現方法はどうなるか?というと

  • exeを叩いて起動させる。
    • 単体exeとして動かす為には、書いたコードをpy2exeなどでパッケージ化する。
  • 画面上の領域をマウスでドラッグして選択する。
    • 画面上の領域をマウスで選択ということで、GUIをサポートする機能が必要。wxPythonなどを使用する。
  • 選択した領域のスクリーンショットを撮影する。
    • 画像処理をサポートする機能が必要。PILなどを使用する。
  • 撮ったスクリーンショットDropboxにアップロードする。
  • アップロードされた画像の公開用URLをブラウザで開く。
    • Dropbox APIで公開用URLを取得後、webbrowserライブラリなどで対応。

という本職の方が見たら何言ってんだコイツレベルの設計が完成。
が、規模的にはこれで十分な気もするのでじゃんじゃんやっていきましょう。