このBlogは移転しました。今後は aish.dev を御覧ください。

PowerPointは僕らのIllustratorだ

普段、図を書いたりすることはそれほどないが、書くときは適当にPowerPointを使って書いてしまうことが多い。このページの図 なんかもPowerPoint製で、けっこう綺麗な図を手軽にかけるので重宝している。

書くのが簡単なのはいいんだけど、書いたあと、画像として出力するのが微妙にめんどくさい。数が多いとうんざりする。ということで、Sphinxの文書を書くときのために、PowerPointのプレゼンテーションから画像を抽出し、png ファイルとして保存するDirectiveを作成してみた。

この拡張は標準の image ディレクティブのパラメータに加え、pptfilenameshapename を指定できる。

.. ppt-shape:: abc.png
   :pptfilename: testppt.pptx
   :shapename: shape-title

pptfilename には、PowerPointプレゼンテーションのファイル名を指定し、shapename には、図形の名前(図形の書式設定|代替テキスト|タイトル で指定した、図形のタイトル) を指定する。Sphinxで文書をビルドする時、プレゼンテーションが更新されている場合は画像を抽出してファイルに保存する。PowerPointがインストールされていない環境では、作成済みの画像をそのまま使用する。

動作環境

インストール

$ git clone git://github.com/atsuoishimoto/pptshape.git
$ cd pptshape
$ python3 setup.py install

使い方

  1. Sphinx の設定ファイル conf.py の、extentions を次のように修正する。
extensions = ['pptshape.directive']
  1. プレゼンテーションの画像は、複数の図形から構成される画像の場合はグループ化して一つの図形に変換し、図形の書式設定で 代替テキスト|タイトル に、画像の名前を指定する。
  2. rst ファイルに、次のディレクティブを記述する。
.. ppt-shape:: abc.png
   :pptfilename: testppt.pptx
   :shapename: shape-title

ppt-shapeディレクティブは、Imageディレクティブと同じ機能をサポートしており、PowerPointがインストールされていない環境(Unix,Macなどを含む)では、Imageディレクティブとまったく同じ動作となる。

ppt-shapeは pptfilename で指定したプレゼンテーションから、shapename というタイトルの画像を検索し、引数として指定した画像ファイルとして出力する。文書のコンパイル時、プレゼンテーションファイルと画像ファイルの更新日付をチェックし、更新された画像ファイルだけを生成する。

動的型言語の使いどころ - その2

先日、動的言語の使いどころ という記事を公開してしまった。公開して「しまった」というのは、じつはあの記事はもうしばらく寝かしてから公開するつもりで、まだ未完成だったためだ。なんで公開するつもりがないエントリが公開されてしまったかというと、エントリを更新してリポジトリにpushすると自動的にサイトが更新されるからくりを仕込んだのをすっかり忘れて、気軽に git push してしまったのだ。ということで、前のエントリで書いておきたかったことをもうちょっと書き足しておきたい。

私は Python を使ってはいるが、それでも、Python 使うやつとか馬鹿じゃねーの、と思うこともそれほど珍しくはない。特に、自分で Python を使うのと、他人が書いた Python アプリケーションを使うのはまったく別の話で、他人に対しては「Python 使うなよ」と言いたくなってしまう時もないではないのだ。

例えば、有名な Python製チケット管理アプリケーションの Trac というのがある。ここ数年は Trac を使っていないが、以前使っていた時には、Trac を管理するのがイヤでイヤで仕方がなかった。Trac は本体は比較的シンプルな機能しか持たず、いろんなプラグインで機能を拡張して便利に使えるタイプのアプリケーションだが、 本体のバージョンアップにプラグインがきちんと追随できていないことが多く、迂闊にバージョンアップしてしまうとあれが動くがこれが動かない、バージョンアップしなくてもこっちのプラグインが旧バージョンでは動かなくなってしまう等々の問題が頻出するのだ。

こういうのは、Javaなんかで書いてあるアプリケーションなら比較的対処しやすい。本体の仕様変更などはプラグインに配慮してそれほど大胆な変更はしないもので、非互換な点があってもとりあえずコンパイルしてエラーが出た部分をちょこっと直してやれば、大概はそれで済む。

しかし、これがPython製だとそうも行かない。動かしてみないことにはどこにエラーが有るかわからないし、テストしようにも、そもそもテストスクリプトがあるのか、あったとしてもどうやって起動するのか、環境をどうやって作るのかというのをいちいち調べなければならない。運良くテストを実行できて問題を検出できたとしても、これを直すのがまた一苦労だ。アプリケーションとプラグインのコードベースをよく知っているならともかく、そうでなければ、どこがどう変更されているのか、GrepやらVCSのログやらを頼りに探しまわらなければならない。これは Python/Tracに限った話ではなく、例えばFirefoxのアドオンがちょっとしたバージョンアップで無効化されてしまったり、VimEmacsの拡張が使えなくなってしまったりということはよくあることだろう。

では、TracJavaで書きなおすべきなのかというと、そうも言い切れない。ダックタイピングの手軽さで、JavaC++ だとプラグインの更新やリビルドが必要となってしまうようなケースでも、Pythonなら修正不要でそのまま使えたりするし、そもそも互換性が問題になるほどたくさんのプラグインが書かれているのは Python で気軽に書けるというのも大きな要因で、これが全部 Java で書かなきゃならんとなったら、こんなに人気が出たかどうかはわからないだろう。

きっと、Tracのようなアプリケーションでは、PyPIなどのリポジトリとの連携、Jenkinsまでは行かないまでも、単純なAPIのバージョンチェックにとどまらない、簡単なテスト実行方法の標準化といった、ソフトウエアのライフサイクル全体への配慮がもっと必要なんだろう。これは別に動的言語に限らず必要ではあるが、Pythonのように手軽に使えることを期待されてしまうプログラミング言語を使うのであれば、その特徴をもっと生かしたアプリケーション作りが要求されるのだと思う。

動的型言語の使いどころ

型宣言というのは良い物だ。型が明確なら、実行速度は型情報がない場合と比べて100倍も200倍も高速になるし、エラーチェックだってできる。リファクタリングも簡単だ。

型がないほうがいいという人は、昔の、まだプロトタイプ宣言がなかった頃のCコンパイラでも使ってみるといい。静的型チェックの有り難みが身にしみることだろう。型宣言を書くのがめんどくさいったって、大したことはない。さすがにC++の型宣言はめんどくさかったが、autodecltype のおかげで楽になった。それでもめんどくさいというなら、Fortranでも使って i ではじまる変数名は宣言不要とか言って喜んでるといい。「暗黒の型宣言」とか言い換えて中二病気分を満喫するのもいいだろう。

しかし、動的な型システムの言語というのも、それはそれで良い物だ。現在、私の主力言語はPythonであり、そのメリットは日々実感している。動的型システムを使った開発スタイルのほうが、静的型システムを使った開発スタイルよりも私の目的には適しているからだ。

アプリケーション開発という面から見た時の、動的型言語のメリットとはなんだろうか。色々とあるが、システム開発全体のライフサイクルへ与える影響を考えると、「不具合のあるアプリケーションを実行できる」ということだと思っている。「そんなアプリ実行しねーよ」と思うかもしれないが、そんなことはない。開発中・修正中のアプリは全て不具合のあるアプリで、この期間中、我々はアプリをだましだまし実行しながら開発・デバッグを進めているのだ。動的型言語をつかうなら、この「だましだまし」のだましやすさを意識し、最大限に活用しなければ意味が無い。

昔からよく言われるプログラミングの格言に、こんなのがある -

「Make it work, Make it right, Make it fast - in that order.」

とりあえずでも何でも、まずはなによりも動くコードを書き、次にそのコードが正しく動くようにする。最後に、パフォーマンス上の問題があれば、適正な時間内に処理できるようにする。「動かす・正しく・速く」と、この順番が重要だ。正しい処理があいまいなうちにコードを最適化しても意味が無いし、正しくないコードでも、動かないコードよりははるかにマシだからだ。

動的型言語は、このスタイルの開発に素晴らしくマッチする。書きやすいところ、書きたいところ、気になってるところなどを気ままに書き散らし、実行して思考内容を実験できる。「Make it work」のステップを、段違いに素早くすすめられるのだ。このメリットを活かせば、ものすごいスピードで実装と実行を繰り返してアプリケーションドメインと問題点を正しく理解し、最終的には「正しい」実装にまでたどり着ける。開発手法としては、トップダウンというよりボトムアップなアプローチに適しているだろう。

もちろん、デメリットもある。「Make it work」が効率的でも、「Make it right」のステップでは不利な面もある。静的型システムならコンパイル時に検出できるエラーもでも、動的型システムだと動かしてみないとわからない。しかし、このような型チェックで検出できるのは、かなり単純なエラーだけで、単純な関数の引数間違いなど、うっかり見逃して本番環境で出してしまうと、背中が汗でびしょびしょになるようなやつだけだ。こういったエラーは単体テストを流すだけでほとんど検出できるし、動的言語ではテストを書くコストが安くつくので、単体テストカバレッジ100%近くを目指すのもそんなに難しいわけではない。動的型言語でも静的型言語でもテストを書く必要性は変わらないわけで、ここは大きな問題ではないと考えている。

ただ、リファクタリングツールなどでコードの自動修正などを行うのは難しいし、静的にコードを解析してコード補完を行うことも難しい。もしPythonが静的な型システムを持っていたら、Python2->3の移行はあっという間に完了したことだろうw。パフォーマンス面の不利はもちろんあるし、JITを使っても万能ではなく、JITのコストでかえって負荷が増大する場合もある。単純に変数の宣言がないから素晴らしいとか、そんな単純な話ではないのである。

OS Xの Vim で IME 制御

Vimで挿入モードから抜ける時に英数入力に切り替える を参考に、KeyRemap4MacBook を使って、ESCを押したらIMEを抜けるように設定。元のままだと、Ctrl+[ で抜けた時に効果が無いようだったので、private.xml ファイルに設定を追加した。

<?xml version="1.0"?>
<root>
  <list>
    <item>
      <name>LeaveInsMode with EISUU(Terminal)</name>
      <identifier>private.app_terminal_esc_with_eisuu</identifier>
      <only>TERMINAL</only>
      <autogen>--KeyToKey-- KeyCode::ESCAPE, KeyCode::ESCAPE, KeyCode::JIS_EISUU</autogen>
      <autogen>--KeyToKey-- KeyCode::C, VK_CONTROL, KeyCode::C, VK_CONTROL, KeyCode::JIS_EISUU</autogen>
      <autogen>--KeyToKey-- KeyCode::BRACKET_LEFT, VK_CONTROL, KeyCode::JIS_EISUU, KeyCode::ESCAPE</autogen>
     </item>
  </list>
</root>

Python3 の数値型

お前ら、Python3 だと、みんな大好き全角数字使えるの知ってた?

>>> int('100')
100
>>> int('100')
100
>>> int('100', 16)
256

ってことは、これがエラーになるのはバグじゃないか?

>>> int('FF', 16)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  ValueError: invalid literal for int() with base 16: 'FF'

追記

Jun Omae⁽⁻⁶⁶ʲ⁵⁾ さんの Tweet によれば

Python3 の数値型 http://www.gembook.org/python3-noshu-zhi-xing.html … Python3 からってわけじゃなくて py24 とかでも int(u'100', 16) #=> 256 ってなりますよ

ということで、ちゃんと試してみたら 2.7 でも全角数字が使えました。一応、このエントリを書く前に 2.7 で試してたんですが、今思えば int('100') しか試してなくて、これはもちろん int(u'100') で試さないと意味がなかったです。

しかしこの機能、気が付かなかったなぁ... そんな昔からあったか... いったい誰得なんだ...

Macbook Air OS X 10.8.2 のふしぎ

Python浮動小数点数を使って大きな値の計算をすると、

>>> 1e100 * 1e100
1e+200
>>> 1e200 * 1e200
inf
>>> 1e200 ** 1e200
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: (34, 'Numerical result out of range')
>>> 1e1000 ** 1e1000
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  OverflowError: (34, 'Numerical result out of range')

こんな感じになる。手元の Linux とかWindowsCygwinでも同じ結果になる。

同じ演算を、買ったばかりの Macbook Air / OS X 10.8.2、13インチ液晶/ 2 GHz Intel Core i7/メモリ8G/SSD 512G で実行すると、次のようになる。

>>> 1e100 * 1e100
1e+200
>>> 1e200 * 1e200
inf
>>> 1e200 ** 1e200
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  OverflowError: (34, 'Result too large')
>>> 1e1000 ** 1e1000
inf

途中までは同じだが、最後の 1e1000 ** 1e10000 が例外ではなく、inf となっている。Python 2.7.3/3.3 どちらでも同じ結果となった。なにか評価の順番でも違うのかもしれないが、Macのビルド環境を整えるのはちょっとめんどくさそうなので、原因は調査していない。

なお、前述の動作環境で、"13インチ液晶/ 2 GHz Intel Core i7/メモリ8G/SSD 512G" の部分は、たんなるスペック自慢である。特に意味は無いので注意されたい。

Python 3.3 からの with 文

以前、Mockライブラリの説明記事 でこんなことを書いた。

コンテキストマネージャで patch() を使う場合、複数のオブジェクトを同時に置き換える時に

def test():
    with patch('testapp.func1') as m1:
        with patch('testapp.func2') as m2:
            with patch('testapp.func3') as m3:
                with patch('testapp.func4') as m4:
                    func()

なんて書くのはみっともない。Python2.xなら、 contextlib.nested() を使ってこう書こう。

from contextlib import nested
def test():
    with nested(
        patch('testapp.func1'),
        patch('testapp.func2'),
        patch('testapp.func3'),
        patch('testapp.func4'))
    as (m1, m2, m3, m4):
        func()

ここで Python2.x なら と書いているが、これは卑怯なごまかしである。実は、Python3 では、contextlib.nested() が廃止されたため、どうにもいまいちな書き方になりがちだったのだ。

Python3 では、`contextlib.nested()` が廃止され、代わりに with 文に複数のコンテキストを指定できるようになった。

def test():
    with patch('testapp.func1') as m1, patch('testapp.func2') as m2:
        func()

これは良いのだが、`with` 文には () が書けないため、たくさんのコンテキストマネージャを with 文に書くのが面倒になってしまった。

本当は、

def test():
    with (patch('testapp.func1') as m1, 
          patch('testapp.func2') as m2,
          patch('testapp.func2') as m3,
          patch('testapp.func2') as m4):

        func()

のように、`()` で全体を囲んで自由に改行出来ればよいのだが、これはシンタックスエラーとなってしまう。上のように書きたければ、行末に \\ をつけて

def test():
    with patch('testapp.func1') as m1, \
         patch('testapp.func2') as m2, \
         patch('testapp.func2') as m3, \
         patch('testapp.func2') as m4:

        func()

とするか、

def test():
    with (patch('testapp.func1')) as m1, (
          patch('testapp.func2')) as m2, (
          patch('testapp.func2')) as m3, (
          patch('testapp.func2')) as m4:

        func()

のように、むりやり変な位置に () を付けるしかなくなってしまった。

「カッコ書けるようにすればいいじゃん」と思うかもしれないが、Python のパーサでこれをやるのは意外と難しい。ここ でも話題になっているが、 普通の式の後ろに as を書けるようにするのは、パーサに手を入れないとうまく行かなさそうだ。そして、パーサにはできるだけ手を入れないというのが Python 界の不文律なのだ。

全体をカッコで囲むのは難しいが、別案としては

def test():
    with (patch('testapp.func1'),
          patch('testapp.func2')
         ) as (
           m1, 
           m2):

        func()

のように、コンテキストマネージャ部分をカッコで囲み、それを受け取る変数名を分離する書き方であれば結構かんたんにできた。しかし、結局は既存の構文と同じ機能でしかないわけで、同じ事をするのに複数の構文を導入するのは意味が無い。

contextlib.ExitStack()

Python 3.3 では、contextmanagernested() に 代わって ExitStack() が導入された。ExitStack() を使えば、こんなかんじでコンテキストマネージャを使用出来る。

from contextlib import ExitStack 
def test():
    with ExitStack() as stack:
        m1 = stack.enter_context(patch('testapp.func1'))
        m2 = stack.enter_context(patch('testapp.func2'))
        m3 = stack.enter_context(patch('testapp.func3'))
        m4 = stack.enter_context(patch('testapp.func4'))

        func()

これならば見た目もすっきりしているし、Python2 の nested() を使ったコードよりも自由度が高い。ということで、`contextlib.ExitStack()` の利用をおすすめする次第である。