グレーな道を進む

半端研究者の備忘録と日記です.主に,アルゴリズムやプログラムに関する話題を書こうと思っています.

Python: 関数を引数とする関数の作り方(+関数の引数を省略したい)

一応,混乱のないようにタイトルを書いたつもりですが.

今回の内容は,Pythonで関数を作る話というよりは,Pythonで"関数"を引数とする関数を作る話です.「そんなことはわかっている」という人は,そのままお読みください.「普通の関数の作り方が知りたいのだが.」っという人は下記のリンクを見てください.

Pythonを学ぼう 第24回 関数の基本 - ほぷしぃ

話を戻します. 関数の引数は用途に応じて,数値,文字列,アドレス値など様々ですが,本記事では関数を引数とする方法について述べます.また,引数とした関数の引数の一部を省略する方法についても述べます.これは,matlabでは,eval関数や@関数を使うことで出来る(また,他の言語でも基本的に出来る).これは非常に便利なので,Pythonもできないと困る.ということで調べてました.

どうやらPythonではfunctoolsのparticalmapを使うことによって可能らしい.mapの使い方が地味に面倒(まあ,Python全般がちょこちょこ面倒だが).

ここでは,二つの数字を足すだけの関数add_func

# add    
def add_func(n, m):
    val = n + m;
    print(" ==in add_func==");
    print(" val=", val);
    return  val;

を対象に,以下の4つを試す.

  1. 普通に関数として(直接的に)実行する
  2. 関数を引数に指定して間接的に実行する
  3. particalとmapを使って間接的に実行する
  4. particalとmapを使って間接的に実行する(一部引数を省略する)

[ソースコード] test3_func.py - Google ドライブ

以降,実行コード内を分割して説明していく(とりあえず実行したい場合は,上記コードを実行).

1. 普通に関数として(直接的に)実行する

以下にコード.特に関数を引数にしているわけでもないので普通に実行するだけ.

n = 10;
m = 20;
print("");

# 関数を直接実行する
print("*** 直接実行 ***** ");
add_func(n,m);
print("");

これを実行すると

f:id:gara14:20160811204124p:plain

といった結果が出力される.引数n=10, m=20が足されてval=30が出力されている.

2. 関数の引数に指定して間接的に実行する

関数を引数にする関数を作るといっても,表面上は意識することはない.次のようにadd_funcを間接的に実行するのための関数test_func

def test_func(func, n, m):
    print("==in test_func==");
    val = func(n, m);
    print(" func(n,m)=", val);

を新たに定義する.test_func()は,見ての通り内部でval =add_func(n,m)と書くことでadd_func()を呼び出している.matlabだとval=eval(add_func, n, m)とする必要があるので,evalを使わない分,Pythonのほうがスマート.これを実行するためのコードは

# (functools, map)を使わない方法
print("*** functools+map 未使用 ***** ");
test_func(add_func, n, m);
print("");

で,これを実行すると

f:id:gara14:20160811204128p:plain

が出力される.出力結果から,test_func→add_funcという順序で実行されていることがわかる. この方法で,関数を引数として扱うことが出来る.ただ,この方法だとadd_funcの引数をtest_funcの引数に使わなければならない.これは結構手間だし,記述が冗長になるので避けたい.要するに,元の関数はどうあれ,test_funcを使う段階で,mが特定値で良いのなら省略したい.例えば,test_func(add_func, n)みたいに.これは,functoolsのparticalとmapを使えば出来るらしい.

3. particalとmapを使って間接的に実行する

参考:functools.Partical

てはじめに,2.と同様のことをparticalとmapを使って実現する.内容としては同じだが,functools.particalを使うためにmapを使う必要があり,そのためにtest_func()の中身を少し修正しなくてはならない.この関数(test2_func)を以下に載せる.

def test2_func(func, n, m ):
    print("==in test2_func==");
    val = list(map(func,[n],[m]))[0];
    print(" func(n,m)=", val);

違いがあるのは,3行目.正直,mapに関しては「第一引数に,他の引数を与えてその結果全てを取得する」関数という程度としてしか認識していない.mapの返し値は実体を持っていないらしく,これを配列にするためlistが追加で必要. 肝心の実行部分は

# (functools, map)を使う方法
print("*** functools+map 使用 ***** ")
add_func_obj = functools.partial(add_func); # オブジェクト化
test2_func(add_func_obj, n, m);
print("");

となる.test2_func()を実行する前に add_func_obj = functools.partial(add_func); と書き,関数をオブジェクト化する.ちなみに,add_func_objをprintで実行すると

f:id:gara14:20160811204145p:plain

と出力される(色々見れるのは楽).先ほどのコードを実行すると,

f:id:gara14:20160811204137p:plain

が出力され,2.0と同じ結果が得られることがわかる.これだけ見ると,particalを使うメリットはないが,この方法だと引数の省略が出来る.次はその話.

4. particalとmapを使って間接的に実行する(一部引数を省略する)

まあ,3.冒頭のリンク先に書いてあるが,この方法だと引数にした関数の引数を省略することが出来る.流れ的には,メイン部分を先に出したほうが良い気がするが,今までと同じで関数→メイン部分の順で載せる. まず,経由する関数は

def test3_func(func, n ):
    print("==in test2_func==");
    val = list(map(func,[n]))[0];
    print(" func(n,m)=", val);

となる.減らす引数(m)がなくなっているだけ.次に,メイン部分は

# (functools, map)を使う方法+関数型引数の一部を固定する
print("*** functools+map 使用(一部値指定)***** ")
add_func_obj = functools.partial(add_func, m=10);
test3_func(add_func_obj, n);
print("");

となる.3.と比べると三行目が異なり,particalの第二引数にm=10が追加されている.感覚的には,m=10を含んだadd_funcのオブジェクト化と捉えている.add_func_objをprintで実行すると

f:id:gara14:20160811204149p:plain

が出力される.引数の一部が指定されているのが見て取れる.で,肝心の実行結果は

f:id:gara14:20160811204140p:plain

が出力される.今までと違いm=10なので,n+m=20となっている.

まとめ

以上が,関数型の引数を使う方法と,関数型引数の一部引数を省略する方法.個人的な感想としては,単に関数を引数として使う場合(2.)には,Pythonのほうが楽に書ける.一方,引数指定の省略する場合(4.)には,matlabのが楽に書ける.といった印象.関数自体(ライブラリ含む)の作りやすさを考慮すると,Pythonが有利か?

Pythonを使い始めて一か月の元matlab使いの感想

記事で書き出したのは最近ですが,Pythonを使いだしてなんだかんだ一か月ぐらい経ちます. その間,会社の測定データを読み込んだり,スパース最適化アルゴリズムを組んだり,乱数列を生成したりした.

そろそろ一度,Pythonについて思ったことを書こうと思った. 以降,私の環境のPython3.5+Spyderを前提に書きます.

Python(+spyder)の使い勝手

個人的には,結構満足しています(というかIPythonが便利).
95%ぐらい満足.本格的にPythonに研究環境移行をすることを決心したぐらい.
ただし,僅かながら不満はあります.以下,満足,不満の順で書きます.

満足な点

ライブラリの選択の自由度が高い,類似関数が多い

良くも悪くもライブラリの選択肢が多い.また,ライブラリの種類の多さはフリーのプログラム言語としては仕方ないし,別に悪いことではないと思います.

類似関数に関して初めはちょっと混乱するが,(上で挙げたCtrl+Iなどの)補助機能が充実しているので問題になりません.逆に,仕様が明確になると類似関数でも出来ることが違うのでそこを生かした使い方が出来ます.この話に関しては,いずれ機会があればもう少し詳しく書きます.

この点を不満点とするか否かが,Pythonに関する付き合い方を決めると思う. 個人的に選択肢の自由度が高いことはメリットです.自分で満足したものを選べる(ない場合は作るのだが)ので.そこが面倒と感じる人には,不満点となるでしょう.

関数(ライブラリ)作成が楽

matlabでは関数を作成する場合は,関数ごとにファイルを作る必要があった. これに対して,Pythonの場合は関数群をまとめて一つのファイルを作成することが出来る.

つまり,matlabだとfuncs1, funcs2という関数を作る場合,

funcs1.m

funcs2.m

というファイルを作る必要があるが,Pythonの場合は

mylib.py

というファイルを作って,この中にfuncs1(), funcs2()という関数を作成すればよい. これは,むしろmatlabが特殊だったという話なのだが,matlabから見たpythonということであえて書きました.

このメリットは,特定のカテゴリに属する関数群を一つのファイルに纏めておけることです. matlabを使っていて,かつ関数を自作しないひとにはあまりメリットを感じないみたいですが...

Ctrl+Iが便利

spyderというか,IPythonのメリットのような気がするが. 関数名の上で「Ctrl+I」すると,関数のヘルプが読める(matlabにおけるhelp XXに相当する). これが非常に便利.あと,説明が過不足なく適切なことが好感を持てる.matlabを使い始めた時の関数説明に対する感触に近い.これで一気にPythonとの距離が縮まりました.

f:id:gara14:20160807105741p:plain

プロットが楽
matplotlibを使うことでmatlabと同じ感覚でプロット出来ます(一部そうでもないですが). これが本当に同じ感覚で書けるので楽です.すごく簡単な例を挙げると,matlabでは

figure(1);
clf;
plot(x);

って書くに対して,pythonでは

figure(1);
clf();
plot(x)

と書くことになります.コード:test_plot_subplot.py - Google ドライブ. どうしても,処理→有効性の確認・検討→改良といった手順は必要になるので,「確認」の効率を上げる意味でプロットの楽さは重要です.ここも好感が持てました(まあ,細かい不満もありますが).

不満足な点

不満点として書くが,本質的にはそこまで問題ではない.改善して欲しいなーくらいの感覚.

図全般:特にsubplot
使い始めたときは,きれいなプロットだと思ったのですが...matlabと比べるとやはりいまいち.特にsubplotを使ったときの,labelの扱いが雑なのがなんとも.実際にプロットすると次のような感じになる(上のコードのコメント部分).

f:id:gara14:20160805191245p:plain

隣の図の軸にラベルが重なってしまう...とはいえ,論文などで図を使う場合は,最終的にイラストレータで加工してから載せるので問題ないっちゃ問題ない.まあ,調査不足感があるのでもう少し探る.

Variable Exploler

変数の中身をmatlabと同じ感覚で見れます.助かります.助かりますが,いくつか不満が...

  • 参照のたびに,別ウィンドウが作成されるのが結構邪魔
  • 上で開いた別ウィンドウは更新されない

プログラムを一度実行して,ちょっと結果を見るくらいなら問題にならないのですが,実行→確認→実行を行う場合には結構困る.変数の別ウィンドウが本当に邪魔.今は,意識的に変数ウィンドウを閉じるようにしている.

実際,気になっているのは上の内容くらい.

ネット検索が面倒

要するに,Python2があるため,検索が地味に面倒. Python2との違いに関して,

print "ああ" → print("ああ")

程度だろ?気にするほどではないな.っと思っていました.

甘く見てました.以下のサイト

Python 2.7.x と 3.x の決定的な違いを例とともに

に書いてありますが,Python3ではunicode関連が結構変わったみたいですね.バイナリ操作関連の調査に苦労しました.他にも,python2との差はあるみたいですが,現実問題にならないと実感沸かないのでノーコメントで.

とりあえず,ここら辺に苦労した私は,記事を見るときにバージョンを非常に気にすることになりました.もっと極端に言うと,何かわからないことを検索するときは,"Pyhton XXX"ではなく"Python3 XXX"とすることになりました.

まとめ

不満点も色々書きましたが,結論としてはPython(+spyder)については概ね満足しています. 個人的には,有償無償を抜きにすればmatlab有利,有償無償を考慮するとpytho有利と感じました.

matlabチックなPython環境ができた

とりあえず,「matlabチックな環境が整ったよ」って話..

Python2 と Python3

他のPython入門者同様,次のことに悩みました.

  • 2と3のどちらのバージョンを使うか

これに関しては色々な方がコメントされていますが,主に多いコメントは

  1. 使えるライブラリが異なる
  2. 記法が異なる(例:printなど)

でしょうか.

結論としては,Python3を使うことにしました. 一番の理由は.Python2が今後更新されないらしいことです.

1.に関しては気になりますが.現状,私がしたいことが「行列計算とmatlab的なプロット」なので,最低限

  • 行列計算: numpy, scipy
  • matlabチックなプロット:matplotlab

が使えれば問題ないということで気にしないことにしました.有用なライブラリならPython3も対応するだろうし.

最終的にどうなったか

色々なサイトを参考にした結果,最終的にPython3.5.1とanaconda3(spyder)[※1]をインストールすることに決定しました(なんか,途中3.4を入れた気もするが確認してみたら3.5.1だった).インストール方法に関しては,他のサイトに詳しく書いてあるので,ここでは私が参考にしたサイトを列挙します.

Python3.4.2のダウンロードとインストール

Spyder(PythonのIDE)を入れてみたところ、とても使いやすい

Anacondaのインストール方法(Windows編)

基本的に,自身のPC環境(例:windows10, 64bit)に対応する欲しいバージョンのインストールファイルをダウンロードして,指示に従えばインストール出来ます(maclinuxはわかりません). ちなみに,私はwindows10(64bit)で,Pythonwindows-3.5.1, Anaconda3(64ビット)を選択しました.

インストールが出来ているか確認

私は,windows10に変えたばかりでコマンドプロンプトを探すのにも一苦労です.時期的にそんな人もいるかもしれないので,細かく書きます.とりあえず,windowボタンのプログラムとファイルの検索に"cmd"と入力して,cmd.exeを見つけてクリックします.コマンドプロンプトが起動するので,画面上に「python -V」と打ち込んでEnterキーを押します.その結果,以下のようにPythonのバージョンとAnacondaのバージョンが出ます.

f:id:gara14:20160803222319p:plain

どうやらちゃんとインストール出来てるみたいです. まあ,インストールしてから結構立つので,出来ているも何もないんだが...

[※1]spyderはPython統合開発環境(Integrated Development Environment; IDE)の一つでmatlabチックな環境に出来るらしい.anaconda3は,spyderや行列計算用のライブラリ(numpy, scipyなど)をひとまとめにしたパッケージ.