Ruby/Python ドキュメント

目次


はじめに

Ruby/Python とは?

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を使うスクリプトです。

require 'ftplib'

ftp = FTP.open('ftp.netlab.co.jp')
ftp.login
ftp.chdir('pub/lang/ruby')
puts ftp.dir
ftp.quit
Rubyを使うと非常に簡単に書けますね。それでは次は、同じことをRuby/Python を用いてPythonのftplibを使って書いたスクリプトです。
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用に書か れたライブラリであるかのように使えるようにする拡張ライブラリなのです。


Ruby から Python オブジェクトを操作する

Ruby/Python を使って、Ruby スクリプトから Python のモジュールやオブジェ クトを操作する方法について説明します。

Ruby/Pythonのロード

require 'python'

Ruby/Pythonを使う場合には、まず最初にRuby/Pythonライブラリをロードし ます。Ruby/Python自体は'python'という名前のライブラリです。 これでRuby/Pythonを使用できる状態になります。 Ruby/Pythonライブラリがロードされると (Object クラス直下に) Py という名前のモジュールが作られます。Ruby/Python の機能は、 基本的にはこの Py モジュールの下のモジュールやクラス、モジュー ル関数として提供されます。

evalexec

Py.eval(PythonExpression)
Py.exec(PythonStatement)
Pythonの機能を呼び出す最も単純な方法はPyモジュールのモジュー ル関数であるevalexecを使う方法です。これらは Pythonの組み込み関数evalとexec文に相当するものです。Py.eval はPythonの式を評価し、その式の値を返します。Py.execはPython の文を実行します。

例:

list = Py.eval('[1, 2, 3]')     # Pythonのリストオブジェクトを返す。
Py.exec('print "hello world"')  # "hello world"を表示する。

メソッド呼び出し

obj.method(...)
obj.method?(...)  # 戻り値はtrueかfalse
Pythonオブジェクトに対しては通常のメソッド呼び出しができます。メソッド名 の後に'?'を付けて呼び出した場合、その戻り値をPythonの真偽値 として判定した値を返します。この記法が必要なのは、Pythonの真偽値とRubyの 真偽値の間に単純な対応関係がないからです。したがって、戻り値として真偽値 を期待する場合は必ず'?'を付けて下さい。(Pythonでは真偽値を 表す特別な値は存在せず、None0や空リストなど が偽を表し、それ以外は真を表します。多くの場合、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')   # シンボル値で指定する場合

ブロック付きメソッド呼び出し

obj.method(...) {|arg| ...}
Pythonのメソッドを呼ぶ時にブロックを与えると、そのブロックから Procオブジェクトが生成され、最後の引数として渡されます。し たがって上の式は以下の式とほぼ等価です。
obj.method(..., Proc.new{|arg| ...})

モジュール

Pythonモジュールをimportする

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モジュールの機能を呼び出す

Pythonモジュールで定義されている関数や属性は、モジュール関数として呼 び出すことができます。

例:

require 'python/math'  # Py::Mathが定義される
Py::Math.sqrt(2)       # 1.41421
Py::Math.pi            # 3.14159

クラスとタイプ

Rubyクラスへのマッピング

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オブジェクト備考
nilNone
true1(1)
false0(1)
StringのインスタンスPython文字列
Integerのインスタンス Plain Intger および Long Integer
Floatのインスタンス Floating Point Number
備考:
(1)
この変換はRubyからPythonへの変換の場合にのみ自動的に行われます。 PythonからRubyへ変換する場合には、0や1は単にIntegerに 変換されます。

Pythonモジュール、クラス、タイプ

Pythonのモジュール、クラス、タイプは、それと対応するRubyの ModuleClassが自動的に定義されます。したがっ てこれらは、その対応するオブジェクト同士で相互に変換されます。

参照渡しされるオブジェクト

上記以外のオブジェクトは参照渡しされます。

PythonオブジェクトがRuby側に参照渡しされる時は、Py::Objectのサブクラ スのインスタンスに変換されます。このオブジェクトはPythonオブジェクトへの 参照を保持するオブジェクトで、PythonオブジェクトへのProxyとして機能しま す。このProxyオブジェクトにメッセージを送ると、対応するPythonオブジェク トへメッセージが転送されます。

RubyオブジェクトをPython側へ参照渡しする時は、Python側からは拡張タイ プのオブジェクトとして見えます。このオブジェクトもRubyオブジェクトへの参 照を保持するProxyとして機能し、メッセージの転送を行います。


リファレンス

モジュール クラス
fukusima@goto.info.waseda.ac.jp
Last modified: Sun May 9 21:47:25 JST 1999