Pure Pythonを設定ファイルとして使う

設定ファイルのフォーマット、というのは割と悩ましい問題である。

JSONはよく使われるし好きなのだけど、変数の置換ができない。 また、割とごちゃごちゃする。 YAMLはすっきりするがPythonの標準モジュールで使えないし、変数置換ができない。 Python標準のconfigparser (iniファイル) はシンプルでわかりやすく変数置換もできるのだが、なんかWindowsっぽいので気が向かない。

変数の置換やループなどのダイナミックな処理を設定ファイルで行うことには反対意見も多そうだが、 私はホスト名とかとにかくDRYに書きたい派なので、Pure Pythonで書かれたスクリプトを設定ファイルとして利用するちょっとしたクラスを作った。

極端な例では、N個(設定ファイルで変更)のプロセスで共有する設定ファイルで、各プロセスが利用するポート番号を指定したりとか、便利ではないかと思われる。例には書いてないけど、定義するデータはdictでもなんでも良い。フレキシブル。

デザイナーの方が絡むようなProjectでは若干厳しいかもしれないが、どうせPythonを触る人が見るんだから、結構わかりやすくていいかと思う。

利用するときには

from configloader import conf

とするだけで(main.py)あたかもただのdictのような感じに使えるので、 PurePythonConfigLoaderなる不気味な名前を意識したり、いちいち

# GEEEEEEEE
config = PurePythonConfigLoader.get_config()  

とかやらなくてもよい。Pythonのmoduleの仕組みは最高だ!

では。

Pure Pythonを設定ファイルとして使う

設定ファイルのフォーマット、というのは割と悩ましい問題である。

JSONはよく使われるし好きなのだけど、変数の置換ができない。 また、割とごちゃごちゃする。 YAMLはすっきりするがPythonの標準モジュールで使えないし、変数置換ができない。 Python標準のconfigparser (iniファイル) はシンプルでわかりやすく変数置換もできるのだが、なんかWindowsっぽいので気が向かない。

変数の置換やループなどのダイナミックな処理を設定ファイルで行うことには反対意見も多そうだが、 私はホスト名とかとにかくDRYに書きたい派なので、Pure Pythonで書かれたスクリプトを設定ファイルとして利用するちょっとしたクラスを作った。

極端な例では、N個(設定ファイルで変更)のプロセスで共有する設定ファイルで、各プロセスが利用するポート番号を指定したりとか、便利ではないかと思われる。例には書いてないけど、定義するデータはdictでもなんでも良い。フレキシブル。

デザイナーの方が絡むようなProjectでは若干厳しいかもしれないが、どうせPythonを触る人が見るんだから、結構わかりやすくていいかと思う。

利用するときには

from configloader import conf

とするだけで(main.py)あたかもただのdictのような感じに使えるので、 PurePythonConfigLoaderなる不気味な名前を意識したり、いちいち

config = PurePythonConfigLoader.get_config()

では。

auto-complete-modeとlinum-modeを共存させる設定

f:id:nullbyte:20150717010300p:plain

Emacs22以降でバッファの左側に行数を表示したい場合

M-x linum-mode

で切り替えるか、全部のバッファで表示させたい場合

(global-linum-mode t)

.emacsに書いておけばいいんだけど、auto-complete-modeとcompany-modeを使ってる場合、ファイルの末尾あたりで作業していると、かちゃかちゃと存在しない行番号が出たり消えたり、変な動きになる。

このStack Overflowの書き込みによるとauto-complete.elが候補を表示するために存在しない空行を追加して、次の瞬間company-modeのポップアップがそれの分の行番号を消しちゃうかららしいのだけど、原因はともかくイライラして使い物にならない。

上の記事では諦めてline-number-modeにすれば?とつれない回答だが実はauto-comple.elにワークアラウンドが実装されていて、

(ac-linum-workaround)

と設定に追加するだけでOK。あなたのEmacs Lifeに再び平穏が訪れるでしょう。

私はel-getを使っているので、el-get-init-dirにinit-auto-complete.elを書いて、上記の設定を追加しました。

ネタ元:結局Stack Overflow。

stackoverflow.com

EmacsでiPython notebookを使う(EIN / Emacs iPython notebook)

iPython notebook、使ってますか。便利ですね。

Emacs以外の開発環境が増えるのが許せないハードコアEmacserのために、EmacsからiPython notebookの編集・実行ができるEmacs iPython Notebook(EIN)をご紹介。

公式サイト

Emacs上でiPython Notebookに接続して、Emacs上で直接iPython notebookの作成・セルの編集、実行、グラフ表示までできるスグレモノです。

先に注意:まだそこそこ不安定なので、大切なNotebookの編集はやめておいたほうがよい

インストール

Emacsにて

M-x el-get-install

einを選択して、インストール。

自前でコンパイルなど、そのほかの方法は公式サイトを確認のこと。

かんたんな使い方。

ipython notebookサーバーはあらかじめ起動しておいて、Emacsから

M-x ein:notebooklist-open

で、ポートを選択して接続。

以下のようにノートブック一覧がバッファに表示される。

カーソルを"New NoteBook"に合わせて新しいNotebookが開かれる。

サンプル

f:id:nullbyte:20150625024246p:plain

こんな感じ。%matplotlib inline やっておけば、グラフも出せます。いいね!・・・・でもEmacsで編集した後、ブラウザで開くとグラフが表示されないので、その場合はブラウザ側で再度セルを実行する必要があり。

主なショートカットキー

  • C-c C-c :選択したセルを実行
  • C-c C-z :インタラプト

  • C-c C-n :次のセルへ

  • C-c C-p :前のセルへ

  • C-c C-a : 上にセルを追加

  • C-c C-b : 下にセルを追加

  • C-c C-s :セルを分割

  • C-c C-m :セルをマージ

  • C-c C-t セルのタイプ(Python, MarkDown, Raw)切り替え

ちなみにM-n, M-pにwordsheet-(next|prev)-input-historyとかいうコマンドが割り当てられていて、 簡単にセルの内容が実行履歴で入れ替えられてしまうので、 私は以下のような設定を.emacsに書いてキーバインドを前後セルの移動と置き換えた。おすすめ。

(eval-after-load 'ein-notebook
  '(progn
     (define-key ein:notebook-mode-map (kbd "M-n")
       'ein:worksheet-goto-next-input)
     (define-key ein:notebook-mode-map (kbd "M-p")
       'ein:worksheet-goto-prev-input)
     (define-key ein:notebook-mode-map (kbd "C-c C-n")
       'ein:worksheet-next-input-history)
     (define-key ein:notebook-mode-map (kbd "C-c C-p")
       'ein:worksheet-prev-input-history))) 

注意したいこと。

  • 重要 ノートブックを保存しないままEmacsを終了させて、確認ダイアログで"保存"を選ぶとnotebookが破壊される事があるので、ちゃんと手動で保存しておきましょう。
  • まだPython以外の他言語カーネルには対応してないっぽい。試してみたところ、あらかじめIJulia用で作っておいたNotebookの編集はできてJuliaセルの実行もできるけど、現在EIN上でカーネルの種類を選んだりはできない。多分。
  • カーネルのリスタート(M-x ein:notebook-restart-kernel-command)がうまくいかない。切断後再接続できない。そういうときは一度保存して閉じ、 ノートブック一覧画面で[STOP}を叩いて終了させてから再度開いている。バグだと思う。

・・・色々あるけど、かなり便利なので、ぜひ試してほしい。

Julia入門 辞書(ハッシュテーブル)、Set型について

Juliaの配列以外の、データ構造について。

Dict (辞書/ハッシュテーブル)

JuliaだとAssociative Collectionというらしい。

現在Stableのver.0.3と、ver0.4でリテラルでの宣言の仕方が変わるので注意したい。

明示的に型を指定しない方法

まず0.3では以下の通り。

julia> versioninfo()
Julia Version 0.3.8
Commit 79599ad (2015-04-30 23:40 UTC)
(略)

julia> anydict = {"a" => 123, "b" => 456}
Dict{Any,Any} with 2 entries:
  "b" => 456
  "a" => 123

julia> normaldict = ["a" => 123, "b" => 456]
Dict{ASCIIString,Int64} with 2 entries:
  "b" => 456
  "a" => 123

{} (中カッコ)で宣言すると、{Any, Any}型の辞書が作られる。
[] (各カッコ)で宣言すると、内容から自動で型を推定してくれる。上の例では、おなじ中身でも後者はDict{ASCIIString,Int64}の型になっていることに注意されたし。

これが、ver.0.4だと

julia> versioninfo()
(略)

julia> anydict = {"a" => 123, "b" => 456}

WARNING: deprecated syntax "{a=>b, ...}".
Use "Dict{Any,Any}(a=>b, ...)" instead.
Dict{Any,Any} with 2 entries:
  "b" => 456
  "a" => 123

julia> normaldict = ["a" => 123, "b" => 456]

WARNING: deprecated syntax "[a=>b, ...]".
Use "Dict(a=>b, ...)" instead.
Dict{ASCIIString,Int64} with 2 entries:
  "b" => 456
  "a" => 123

julia> Dict("a" => 123, "b" => 456)
Dict{ASCIIString,Int64} with 2 entries:
  "b" => 456
  "a" => 123

ver.0.3の書き方は両方depricated警告が出る。
後者の型を自動で推理してくれる宣言の仕方は、Dict(key => value, ...) になり、前者の{Any, Any}型でのリテラルの宣言は、方法自体が廃止になった。

型を指定して宣言する方法

これも、ver0.3 / 0.4で異なる。ver.0.3では以下の通り。

julia> versioninfo()
Julia Version 0.3.8
Commit 79599ad (2015-04-30 23:40 UTC)
(略)

julia> (ASCIIString=>Int64)["abc" => 123, "def" => 456]
Dict{ASCIIString,Int64} with 2 entries:
  "abc" => 123
  "def" => 456

ver.0.4では以下のとおり。

julia> versioninfo()
Julia Version 0.4.0-dev+5114
Commit 894a31e* (2015-05-30 03:57 UTC)
(略)

julia> Dict{ASCIIString, Int64}("abc" => 123, "def" => 456)
Dict{ASCIIString,Int64} with 2 entries:
  "abc" => 123
  "def" => 456

というわけで、中身を指定しながら宣言したい場合に、現在ver.0.3 / 0.4で統一して使用できる記法がないのでした。

どうしても統一したい場合には、以下のように先に空のDictをDict{K, V}()というかたちで宣言して、そこに値を追加する方法が良いと思う。

julia> my_dict = Dict{UTF8String, Int64}()
Dict{UTF8String,Int64} with 0 entries

julia> my_dict["キー"] = 123
123

julia> my_dict["別のキー"] = 456
456

julia> my_dict
Dict{UTF8String,Int64} with 2 entries:
  "別の… => 456
  "キー" => 123

julia> my_dict["キー"]
123

この方法は、ver.0.3 / ver.0.4共通で利用できる。

Dict関連の関数

ざっくりいって、pythonのdict関係と似た関数が用意されている。
Juliaはクラスの概念がないので、第一引数にDictを指定する。

詳しくはver.0.3のドキュメントおよび ver0.4のドキュメントを参照のこと。

よく使いそうな関数の例を以下にあげておく。

julia> get(dict, "unknown", "key not found")  # 見つからなかった時のデフォルト値が指定できる
"key not found"

julia> # キーが見つからなかった時の挙動は、関数でも指定できる(do構文)
       get(dict, "unknown") do
         println("key not found!")
       end
key not found!

julia> haskey(dict, "foo")   # キーがあるかどうかチェック
true

julia> delete!(dict, "foo")   # 指定したキーの項目を削除
Dict{ASCIIString,ASCIIString} with 2 entries:
  "bar" => "456"
  "baz" => "789"

julia> keys(dict)    # キーのイテレータを取得
KeyIterator for a Dict{ASCIIString,ASCIIString} with 2 entries. Keys:
  "bar"
  "baz"

julia> values(dict)    # 値のイテレータを取得
ValueIterator for a Dict{ASCIIString,ASCIIString} with 2 entries. Values:
  "456"
  "789"

get関数は、キーがみつからなかった時にdefault値を返すだけだけど、get!関数はみつからなかった場合は指定したキーdefault値またはdefault関数の実行結果を設定する。

julia> dict
Dict{ASCIIString,ASCIIString} with 3 entries:
  "bar" => "456"
  "baz" => "789"
  "foo" => "123"

julia> get!(dict, "NEW_KEY", "NEW_VALUE")
"NEW_VALUE"

julia> dict
Dict{ASCIIString,ASCIIString} with 4 entries:
  "bar"     => "456"
  "NEW_KEY" => "NEW_VALUE"
  "baz"     => "789"
  "foo"     => "123"

julia> get!(dict, "ANOTHER_KEY") do
         return "ANOTHER_VALUE"
       end
"ANOTHER_VALUE"

julia> dict
Dict{ASCIIString,ASCIIString} with 5 entries:
  "bar"         => "456"
  "NEW_KEY"     => "NEW_VALUE"
  "baz"         => "789"
  "ANOTHER_KEY" => "ANOTHER_VALUE"
  "foo"         => "123"

Set型

pythonのsetと同様、重複がないコレクション型がSet。
Arrayの重複削除くらいしか使わないかな、普段は。

julia> anyset = Set()    # 型を指定しないとAny型のSetになる
Set{Any}({})

julia> floatset = Set{Float64}()    # 型を指定する方法
Set{Float64}({})

julia> strset = Set(["foo", "bar", "baz"])    # 型を自動設定する方法
Set{ASCIIString}({"bar","baz","foo"})

julia> push!(strset, "NEW_VALUE")    # 新しい値の追加
Set{ASCIIString}({"NEW_VALUE","bar","baz","foo"})

julia> push!(strset, "foo")    # 同じ値は追加しても重複しない
Set{ASCIIString}({"NEW_VALUE","bar","baz","foo"})

julia> [v for v in strset]    # 配列への変換
4-element Array{Any,1}:
 "NEW_VALUE"
 "bar"      
 "baz"      
 "foo"

以上です。

Julia入門 数字・文字列の組み込み型について

Juliaの組み込み型について。
http://nullbyte.hatenablog.com/entry/2015/05/30/001107:title:前回からの流れで辞書型について調べようか思ったけど、型の話を先にちょっと押さえておいたほうがいいかなと。

数字の型

以下のような型が用意されている。

意味
Bool 真偽値
Int8 8bitの符号あり整数
Uint8 8bitの符号なし整数
Int16 16bitの符号あり整数
Uint16 16bitの符号なし整数
Int32 32bitの符号あり整数
Uint32 32bitの符号なし整数
Int64 64bitの符号あり整数
Uint64 64bitの符号なし整数
Int128 128bitの符号あり整数
Uint128 128bitの符号なし整数
Float16 16bitの浮動小数点型
Float32 32bitの浮動小数点型
Float64 64bitの浮動小数点型
Complex64 64bitの複素数
Complex128 128bitの複素数

整数を宣言した場合、64bit環境ではデフォルトInt64の整数になる。
小数点付きの数字はFloat64になる。
複素数グローバル変数のimに入っていて、デフォルトではComplex64になる。

julia> typeof(256)
Int64

julia> typeof(1.234)
Float64

julia> typeof(2 * im + 1)
Complex{Int64} (constructor with 1 method)

デフォルト以外の型の数字が欲しい時には、各型を小文字にした関数がコンストラクタとして用意されているので、それを使って生成する必要がある。

julia> c = int128(2) ^ 127 - 1
170141183460469231731687303715884105727

julia> typeof(c)
Int128

ちなみに、128bitで収まらないような巨大な数字を計算したい場合は(RSA暗号とか?)、BigInt / BigFloatという型が使える。
(詳しくは公式ドキュメントを参照のこと)

julia> intval = typemax(Int128)     # typemax: 指定した型の最大値を返す
170141183460469231731687303715884105727

julia> intval + 1      # 桁あふれしてしまう
-170141183460469231731687303715884105728

julia> bigintval = BigInt(intval)
170141183460469231731687303715884105727

julia> bigintval + 1     # OK
170141183460469231731687303715884105728

上記の例のように、Juliaでは型の上限値を超えた値はを代入すると桁あふれする。
必要な型をあらかじめ考えておくこと。

数字に関する組み込み関数は公式ドキュメント(数学計算数字関係)を参照。

文字列

ASCII文字列のASCIIString型、UTF8文字列のUTF8String型がある。

julia> ascii_str = "abc"
"abc"

julia> typeof(ascii_str)
ASCIIString (constructor with 2 methods)

julia> utf8_str = "文字列"
"文字列"

julia> typeof(utf8_str)
UTF8String (constructor with 2 methods)

ちなみに、ASCIIString型とUTF8String型でも、結合などはそのまま行うことができる。

julia> ascii_str * utf8_str      # 結合
"abc日本語"

また、一文字を表すChar型もあり、リテラルではシングルコーテーションで宣言する。

julia> ch = 'c'
'c'

julia> typeof(ch)
Char

julia> not_ch = "c"
"c"

julia> typeof(not_ch)    # ダブルコーテーションで宣言すると、Stringになる
ASCIIString (constructor with 2 methods)

julia> string(ch, "har")    # CharとStringは*では結合できないので、string関数を使う
"char"

julia> ch * "har"
ERROR: `*` has no method matching *(::Char, ::ASCIIString)

julia> "some_string"[4]    # Stringから添え字アクセスした時の返り値の文字列もCharになる
'e'

julia> typeof("some_string"[4])
Char

そのほか、文字列型用の関数の一覧は公式ドキュメントを参照のこと。