Ruby/PythonはRubyにPythonインタプリタをライブラリとして埋め込んでしま う、Ruby の拡張ライブラリです。Ruby/Python を使うと、Ruby スクリプトから Python 用に作られたライブラリを利用することができます。
Ruby/Python は二つの言語を出来る限り透過的に繋げるように作られていま
す。したがって、Python のライブラリを利用するのに、特別な配慮や面倒なコー
ディングはほとんど必要ありません。Ruby 用に作られたライブラリを利用する
のとほとんど変わらない感覚で使用できます。
簡単な例
まず簡単な例としてftplibを使用して、FTPサーバにアクセスするRubyスクリ プトを見てみましょう。ftplib自体はRubyにもPythonにもそれぞれの言語用のも のが標準で添付されています。したがって、Ruby/Pythonを使ってPythonの ftplibを使うこと自体には実用的な意味はありません。でも、比較のためにはもっ てこいです。
まずはRuby用にRubyで書かれたftplibを使うスクリプトです。
Rubyを使うと非常に簡単に書けますね。それでは次は、同じことをRuby/Python を用いてPythonのftplibを使って書いたスクリプトです。require 'ftplib' ftp = FTP.open('ftp.netlab.co.jp') ftp.login ftp.chdir('pub/lang/ruby') puts ftp.dir ftp.quit
require 'python' require 'python/ftplib' ftp = Py::Ftplib::FTP.new('ftp.netlab.co.jp') ftp.login ftp.cwd('pub/lang/ruby') ftp.dir ftp.quit
最初の例と比べて見てください。どうですか?それほど違いはありませんよ ね。でもこの後者の例はPython用に書かれたライブラリを使っているのです。
大きく違う部分は冒頭のrequire
でライブラリをロードする部
分と、FTP
クラスの名前を指定している部分です。それ以外の部分
はメソッド名が若干異なるだけです。Rubyを良く知っている人なら、これだけで
Ruby/Pythonをどうやって使うのか大体分かってしまうと思います。このように
Ruby/PythonはPython用に書かれたライブラリを、あたかもそれがRuby用に書か
れたライブラリであるかのように使えるようにする拡張ライブラリなのです。
require 'python'
Ruby/Pythonを使う場合には、まず最初にRuby/Pythonライブラリをロードし
ます。Ruby/Python自体は'python'
という名前のライブラリです。
これでRuby/Pythonを使用できる状態になります。
Ruby/Pythonライブラリがロードされると (Object
クラス直下に)
Py
という名前のモジュールが作られます。Ruby/Python の機能は、
基本的にはこの Py
モジュールの下のモジュールやクラス、モジュー
ル関数として提供されます。
eval
と exec
Pythonの機能を呼び出す最も単純な方法はPy.eval(PythonExpression) Py.exec(PythonStatement)
Py
モジュールのモジュー
ル関数であるeval
とexec
を使う方法です。これらは
Pythonの組み込み関数evalとexec文に相当するものです。Py.eval
はPythonの式を評価し、その式の値を返します。Py.exec
はPython
の文を実行します。
例:
list = Py.eval('[1, 2, 3]') # Pythonのリストオブジェクトを返す。 Py.exec('print "hello world"') # "hello world"を表示する。
Pythonオブジェクトに対しては通常のメソッド呼び出しができます。メソッド名 の後にobj.method(...) obj.method?(...) # 戻り値はtrueかfalse
'?'
を付けて呼び出した場合、その戻り値をPythonの真偽値
として判定した値を返します。この記法が必要なのは、Pythonの真偽値とRubyの
真偽値の間に単純な対応関係がないからです。したがって、戻り値として真偽値
を期待する場合は必ず'?'
を付けて下さい。(Pythonでは真偽値を
表す特別な値は存在せず、None
や0
や空リストなど
が偽を表し、それ以外は真を表します。多くの場合、Pythonでは偽を表すのには
0
が使われるようですが、0
はRubyでは真です。)
例:
dict = Py.eval('{"One": 1, "Two": 2}') # Pythonの辞書(ハッシュ) dict.keys # ["One", "Two"] dict["Two"] # 2 dict.has_key("Three") # 0 (Rubyでは真) dict.has_key?("Three") # false
obj.method([...,] Py::AS_KEYWORD, key1 => val1, key2 => val2, ...) obj.method([...,] Py::KW, key1 => val1, key2 => val2, ...)
Pythonのメソッドにはキーワード引数を渡すことができます。しかし、現時
点ではRubyはキーワード引数をサポートしていません。そこで、Ruby/Pythonで
は上記のような呼び出し方をすることにします。この方法では
Py::AS_KEYWORD
という定数を使います。(略記法として
Py::KW
という名前もあります。) 引数リストの中に
Py::AS_KEYWORD
がある場合には、それ以降の引数はキーワード引
数を含むハッシュであると解釈されます。各キーワードの名前は文字列かシンボ
ル値で指定します。
例:
ftp = Py::Ftplib::FTP.new ftp.connect(Py::AS_KEYWORD, 'host' => 'ftp.netlab.co.jp') # 文字列で指定する場合 ftp.login(Py::KW, :user => 'ftp', :passwd => 'address') # シンボル値で指定する場合
Pythonのメソッドを呼ぶ時にブロックを与えると、そのブロックからobj.method(...) {|arg| ...}
Proc
オブジェクトが生成され、最後の引数として渡されます。し
たがって上の式は以下の式とほぼ等価です。
obj.method(..., Proc.new{|arg| ...})
require 'python/module'
Ruby/Pythonライブラリは組み込み関数の require
を置き換え
ます。置き換えられた require
は、'python/'
で始
まるライブラリ名が引数として渡されると、特別な動作をします。(それ以外の
場合は通常のrequire
と同じ。) 'python/'
で始まる
ライブラリ名が渡されると、スラッシュの後に指定された名前の Python モジュー
ルを import します。これは Python スクリプト上で以下の文を実行することに
相当します。
import module
importされたPythonモジュールは、RubyスクリプトからはPy
モ
ジュールの下で定義されているRubyモジュールとして見えます。Pythonモジュー
ルの名前が小文字で始まる場合は、先頭の文字を大文字にした名前で定義されま
す。またPy
モジュールに対する属性参照によってそのモジュール
にアクセスすることも出来ます。
例: Python の sys モジュールを参照する方法
require 'python' # Pyが定義される require 'python/sys' # Py::Sysが定義される Py::Sys # 定数による参照 Py.sys # Pyモジュールに対する属性参照。Py::Sysと同じ
Pythonモジュールで定義されている関数や属性は、モジュール関数として呼 び出すことができます。
例:
require 'python/math' # Py::Mathが定義される Py::Math.sqrt(2) # 1.41421 Py::Math.pi # 3.14159
Pythonのクラスとタイプに対しては、それと対応するRubyクラスが自動的に
定義されます。こららのクラスは全てPy::Object
クラスのサブク
ラスとして定義されます。つまり全てのPythonオブジェクトは
Py::Object
クラスに属しているように見えます。
例:
list = Py.eval('[1, 2, 3]') # Pythonリストタイプのオブジェクト list.type # Py::Types::ListTypeクラス list.is_a?(Py::Object) # true
各Pythonクラスに対応するRubyクラスに定義されているクラスメソッド
new
を呼び出すことでクラスのインスタンスを生成できます。
new
に渡された引数は、Pythonのインスタンス生成の引数として使
われます。
例:
require 'python/ftplib' ftp = Py::Ftplib::FTP.new('ftp.netlab.co.jp') # 以下のようなPythonのインスタンス生成に相当 # ftplib.FTP('ftp.netlab.co.jp')
オブジェクトはメソッド呼び出しの引数として渡される時に、RubyとPython の間を行き来することがあります。その際に、オブジェクトは移動先の言語のオ ブジェクトに自動的に変換されます。またメソッド呼び出しの戻り値として戻る 時には逆の変換が行われます。
以下のオブジェクトは、それぞれの言語のネイティブなオブジェクトに変換 されます。つまり値渡しになります。
Rubyオブジェクト | Pythonオブジェクト | 備考 |
---|---|---|
nil | None |
|
true | 1 | (1) |
false | 0 | (1) |
Stringのインスタンス | Python文字列 | |
Integerのインスタンス |
Plain Intger および Long Integer | |
Floatのインスタンス |
Floating Point Number |
Integer
に
変換されます。
Pythonのモジュール、クラス、タイプは、それと対応するRubyの
Module
やClass
が自動的に定義されます。したがっ
てこれらは、その対応するオブジェクト同士で相互に変換されます。
上記以外のオブジェクトは参照渡しされます。
PythonオブジェクトがRuby側に参照渡しされる時は、Py::Objectのサブクラ スのインスタンスに変換されます。このオブジェクトはPythonオブジェクトへの 参照を保持するオブジェクトで、PythonオブジェクトへのProxyとして機能しま す。このProxyオブジェクトにメッセージを送ると、対応するPythonオブジェク トへメッセージが転送されます。
RubyオブジェクトをPython側へ参照渡しする時は、Python側からは拡張タイ プのオブジェクトとして見えます。このオブジェクトもRubyオブジェクトへの参 照を保持するProxyとして機能し、メッセージの転送を行います。