2012-01-29
tse - Text Stream Editor in Python
久々にsedを使ってちょっとしたテキストを処理をしてたら、やっぱりこういうのもPythonで出来ないかなぁ思った訳である。Pythonスクリプトというのは改行とインデントが不可欠なので One Liner に向かないというのはご案内の通りだが、ちょっとは何とかなるんでないか。
昔、私がPythonを使い始めた頃のPythonはとことんOne Linerに向いていなくて、そんなこと考える自体時間の無駄だったが、今はだいぶ状況が違う。文字列にはちゃんとメソッドがあるし、リスト内包も使える。AWKみたいなマッチング機能とあわせて使えるようにすれば、意外と使い物になるんではないかというアイデアだ。
作ってみると、結構簡単にプロトタイプができた。
http://pypi.python.org/pypi/tse
tse ではAWKのように、条件と条件が満たされた時に実行される処理のペアを
tse -a CONDITION1 ACTION1 -a CONDITION2 ACTION2 ...
のような形式で指定する。
例えばこんな感じだ。
$ cat testfile | tse -a "^\D+" "print L" -a "^\d+" "print S0"
このサンプルでは、二つの -a オプションが指定されていて、最初のオプションは
"^\D+" "print L"
となっている。最初の ^\D+ は「数字以外の文字で始まる文字列」を示す正規表現式であり、この条件にマッチする行では、アクション print L が実行される。アクションはPythonのステートメントで、この例ではPythonのprintステートメントである。Lは現在の行を格納する変数で、AWKでは $0に相当する。
2番目の
"^\d+" "print S0"
では、条件として正規表現 ^\d+ を指定している。これは「数字で始まる文字列」を示す正規表現で、print S0 は条件式にマッチした文字列を出力するアクションだ。S0 は条件式にマッチした部分文字列の変数である。
次の例は、カレントディレクトリに含まれているファイルの拡張子一覧を取得している。
find . -type f|tse -a ".*" "print path.splitext(L)[1]"|sort|uniq
いちいち正規表現を書いてファイル名から拡張子を取り出そうとすると面倒なものだが、ここではPythonの標準モジュール os.path.splitext()を使ってあっさりと取得している。osモジュールは起動時に自動的にimportされるので、インポート処理を記述する必要はない。sys, os, reモジュールは自動的にインポートされるようになっている。
2012-01-17
菊地時夫先生への、GNU Mailmanコミュニティからのメッセージ
すでにご存じの方も多いと思いますが、PythonやApache、Mailmanなど、様々なオープンソースコミュニティで活躍なさっていた、元高知大の菊地時夫 名誉教授が、平成24年1月14日にお亡くなりになりました。
菊地先生はGNU Mailmanという、メーリングリストを運営するソフトウエアの国際化・日本語対応で大変大きな活躍をなさっておりました。GNU Mailmanはこの種のソフトウエアではおそらく最大のシェアを占めており、日本語でメールを利用されている方ならば、知らないうちに菊地先生の貢献の恩恵を受けている可能性は非常に高いのではないかと思います。
今回、菊地先生の訃報にあたり、GNU Mailmanの開発者から弔辞が寄せられました。
http://wiki.list.org/display/COM/TokioKikuchi
以下に、このページの拙訳を掲載します。
去る2012年1月14日、菊地時夫さんがお亡くなりになりました。菊地さんはGNU Mailmanの国際化全般と日本語のサポートに非常に大きく寄与し、GNU Mailmanだけでなく、PythonコミュニティやApacheコミュニティにも大きな功績を残しています。彼はMailmanの協力者リストでは、Mailmanの「気象予報士」の称号を得ています。私たちの感謝と、私たちの想いを、菊地さんとそのご遺族、知り合いの方々にお届けします。
Barry Warsaw さんからのメッセージ
菊地時夫さんはMailmanの初期の国際化作業を助けてくれました。菊地さんから送られた、日本語サポートの最初の頃のパッチをテストしたのを覚えています。何年か前のPythonコンファレンスで、ブランチを実行すると見慣れたMailmanの管理画面が日本語で表示されたのです。私はとても嬉しく思いました。私にはその画面は読めませんでしたが、たまたま隣に座っていた日本人が、これは確かに日本語だと教えてくれました。私は非常に嬉しく思いましたし、彼がMailman全体と、特に国際化対応に残してくれた貢献を、誇りに思います。お悔やみ申し上げます。
Mark Sapiro さんからのメッセージ
とても悲しいニュースです。私が最初にMailmanに関わったとき、菊地さんは2.1ブランチの管理責任者でした。菊地さんはMailmanに関する豊富な知識を持ち、とても助けになる方でした。特に、日本のMUAと、MUAから実際に作成されるメッセージの独特な特徴などについて詳しい知識をもっており、プログラムの修正が及ぼす影響を調査する際に大変役立ちました。最近では菊地さんはMailman以外の活動を中心とされていましたが、それでも役に立つ貢献を続けて頂いていました。哀悼の意を表します。
2011-12-22
僕たちPythonistaは、プログラミングに、Excelを使います!
みんな、Excel使ってますか!?もちろん使ってますね!?休暇届も、作業日報も、見積書も、スケジュール表も、みーんなExcelですね!? もちろん、プログラミングだってExcelですね!? 当然です!みんなそうやって仕事してるんです! Excel版のTwitter公式クライアントはまだリリースされないんでしょうか!?
だけど、Excelでコーディングしてると、ときどきちょっとだけ不便に感じることがありますね!いちいちテキストファイルに落としてから実行しなければならないからです!テキストファイルなんて低級なフォーマットには関わりたくないですね!
でもPythonなら!Pythonなら大丈夫!Pythonなら直接Excelファイルを実行できます!Pythonにはパッケージの配布フォーマットとしてeggという形式が普及していますが、eggはもう古いですね! これからはxls形式を使いましょう!xlsフォーマットでスクリプトを配布、実行しましょう!
使い方は簡単!まず、次のコードをexcelimporter.pyという名前で保存します!
import os, sys, imp import win32com.client sys.path_importer_cache.clear() xl = win32com.client.Dispatch("Excel.Application") class ExcelImp: @classmethod def get_importer(cls, path): if os.path.splitext(path)[1] in ('.xls', '.xlsx'): ret = cls() ret.wb = xl.workbooks.Open(path) return ret else: raise ImportError def __del__(self): if self.wb: self.wb = None def find_module(self, fullname, path=None): for name in self.wb.names: name, ext = os.path.splitext(name.name) if name == fullname and ext == '.py': return self def load_module(self, fullname): code = self._readcode(fullname) mod = sys.modules.setdefault(fullname, imp.new_module(fullname)) mod.__file__ = "<%s>" % self.__class__.__name__ mod.__loader__ = self exec code in mod.__dict__ return mod def _readcode(self, fullname): name = self.wb.names[fullname+'.py'] ret = [] for row in name.RefersToRange.value: row = "".join(("\t" if s is None else s) for s in row) ret.append(row) return "\n".join(ret) sys.path_hooks.append(ExcelImp.get_importer)
保存したexcelimporter.pyを、環境変数PYTHONSTARTUPに指定します!こんな感じですね!
set PYTHONSTARTUP=c:\script\excelimporter.py
次にExcelファイルを用意しましょう!ワークシートにスクリプトを書き、スクリプトのセル範囲に名前を付けます!この時、名前は末尾に「.py」を付けてください!「excelmodule.py」みたいな感じです!
Excelファイルが出来たら、このファイルを今度は環境変数PYTHONPATHに指定します!
set PYTHONPATH=c:\script\fib.xls
これで準備OK!実行してみましょう!
>>> import fib1 # fib.xlsファイルから、fib1モジュールをインポート >>> fib1.fib1(10) # 実行! 89 >>>
素晴らしい!もうテキストファイルなんかとはおさらばです!レッツエンジョイ!エンジョイExcelライフ!
2011-12-20
Python3 Advent Calendar 二十日目 PEP 380 -- Syntax for Delegating to a Subgenerator
Python3 Advent Calendarとやらに参加しろ、なんか書けと言われて泣く泣くキーボードを叩くatsuoishimotoです。
書けと言われてもネタがないのでどっかからパクろうと Python3 Advent Calendar を最初から目を通してみると、しょっぱなから良いネタがあった。何と初日の @shomah4aさんの エントリ。これをパクってしまおう。私のように清く正しい振る舞いを日頃から心がけてさえいれば、このようにあっさりとパクリの元ネタを見つけることが出来るのです。
元ネタの Python とジェネレータ関数 では Python 3.3に組み込まれる予定の PEP 380 -- Syntax for Delegating to a Subgenerator が紹介されているが、これをもうちょっと詳しく説明してみよう。
イテレータの委譲
これまでの Python では、ジェネレータの中で他のジェネレータの値を返すとき、こんな感じで書く必要があった。
def gen(): for v in subgenerator(): yield v
これを、Python3.3 からは
def gen(): yield from subgenerator()
と書ける。また yield from にはジェネレータだけではなく、普通のイテレート可能なオブジェクトも指定できる。
# Python 3.2までのジェネレータ def gen_3.2(): l = [1,2,3,4,5] for v in l: yield v # Python 3.3までのジェネレータ def gen_3.3(): yield from [1,2,3,4,5]
ジェネレータの戻り値
yield from はループを省略するためだけに追加されたわけではない。こんなコードを見てみよう。
@defer.inlineCallbacks def connectpop3(pop3, user, passwd): yield pop3.login(user, passwd) yield pop3.listSize() uids = yield pop3.listUID() for i in range(len(uids)): lines = [] yield pop3.retrieve(i, lines.append) mail = b"\r\n".join(lines) do_mail(mail)
Twisted の inlineCallbacks を使って、POP3サーバからメールを取得する処理だ。一見、普通の関数に見えるかもしれないが、POP3サーバへのログイン等はジェネレータを使ってすべて非同期に実行されるようになっている。なっている、というか、Twisted はまだ Python3 をサポートしていないので、実はまだ「なってない」のだが。
このコードをちょっと整理して、メールの本文を取得する部分を別の関数に切り出してみるとしよう。
@defer.inlineCallbacks def retrieveMail(pop3, ret): lines = [] yield pop3.retrieve(i, lines.append) mail = b"\r\n".join(lines) ret.append(mail) @defer.inlineCallbacks def connectpop3(pop3, user, passwd): yield pop3.login(user, passwd) yield pop3.listSize() uids = yield pop3.listUID() for i in range(len(uids)): ret = [] for y in retrieveMail(pop3, ret): yield y mail = ret[0] do_mail(mail)
現在のPythonではこんな感じで、取得したメール本文を呼び出し元に返すために、わざわざリストを渡してその中に設定して返す、などと面倒くさい手段が必要となってしまう。ジェネレータは値を returnできないし、yieldで戻す値は非同期通信処理の制御に使ってしまっているからだ。
そこでPEP380では、ジェネレータでも値を返せるように変更された。上のコードはこのようにすっきりと書けるようになったのである。
@defer.inlineCallbacks def retrieveMail(pop3): lines = [] yield pop3.retrieve(i, lines.append) return b"\r\n".join(lines) @defer.inlineCallbacks def connectpop3(pop3, user, passwd): yield pop3.login(user, passwd) yield pop3.listSize() uids = yield pop3.listUID() for i in range(len(uids)): ret = [] mail = yield from retrieveMail(pop3, ret) do_mail(mail)
ジェネレータプロトコル
Python2.5からジェネレータオブジェクトにsend()などのメソッドが追加され、ジェネレータ外から情報を送り込めるようになった。
>>> def gen(): ... value = yield 1 ... value = yield value * 2 ... value = yield value * 3 ... >>> g = gen() >>> g.next() 1 >>> g.send(10) 20 >>> g.send(20) 60
この程度の処理なら必要ないが、大きめな処理ではジェネレータを複数の小さなジェネレータに分割したくなる。しかし、そう簡単には分割できない。
def func1(): value = yield 1 # valueをここで受け取りたいが return value def gen(): for v in func1(): value = yield v # 実際にはここで受け取ってしまう
問題は、ジェネレータのsend()メソッドで値を送り込めるのは、最後のyieldだけ、という所だ。この例では、本来func1()内のyieldに値を送りたいのだが、gen()でしか受け取れないため、いったん受け取った値を手動で子ジェネレータにsend()しなければならない。分割できないわけではないが、なかなか面倒なスクリプトになる。
そこで yield from だ。値を個別にyieldするのはやめて、yield fromでまとめて委譲すれば、直接末端のジェネレータに値を送り込めるようになる。
def func1(): value = yield 1 return value def func2(value): value = yield value * 2 return value def func3(value): value = yield value * 3 return value def gen(): value = yield from func1() value = yield from func2(value) value = yield from func3(value)
というわけで
以上で Python3 Advent Calendar 二十日目を終わる。二十一日目は突然 @aodag先生に捕獲されてしまって大変気の毒な @ryoaitaさんにお願いしよう。
2011-12-11
Motorola Photonに乗り換えた
android, desire, x06ht, photon, ISW11M
携帯を、HTC DesireからMotorola Photonに乗り換えたので、感想など。DesireとPhotonではAndroid 2.2と2.3の違いもあるので単純には比較できないが。
- Photonの方が断然バッテリの持ちが良い
- 速い。DesireではWeb版Twitterは実用的な速度で表示されなかったけど、Photonでは普通に使える。
- Desireの有機ELの方が色は綺麗だけど、昼間の屋外での視認性が悪すぎて携帯で使うにはちょっと。Photonは発色はイマイチでも、一日中ちゃんと読める
- ディスプレイ4.2インチは嬉しい。Android版Kindleが読みやすい
- Desireのハードキーが恋しい。DesireとPhotonでメニューキーとホームキーの位置が逆なのがつらい
- WiMaxをオンにしていると、たまに手動でWiMax接続しろって出るのが邪魔臭い
- au WiFi自動接続ツールがオンになっていると、Wifi接続がブチブチ切れる気がする
- デフォルトで夜間はバッテリ節約モードになっていて、これだと勝手にWifi切れたりWiMax探しにいったりして邪魔臭い
- ブラウザでページをロードするとき、DNSの応答待ち(?)の場合なんかで、ロード中のアニメーションが出ないので、ボタンを押し損なったかと思ってしまう
- 標準アプリが雑。使いにくいし、見た目が綺麗じゃないし、翻訳も適当。この点では、HTCは非常に良い仕事をしていると痛感した。
- カメラについてはあまり詳しくないが、あんまり画質が良いとは感じなかった。ちょっと昔のau携帯でとった写真の方が綺麗に感じるのが不思議。カメラアプリもいろいろ使いにくい。
こんなことろか。全体的にはPhotonに満足してるんだけど、荒削りというか、雑な面が目立つのが残念だ。
