Ruby/Python is a Ruby extension library to embed Python interpreter in Ruby. With this library, you can use modules written for Python from your Ruby scripts.
Ruby/Python is designed to integrate the two languages as
transparently as possible. Thus you need neither bothersome coding nor
special consideration to use Python modules. It is much the same as
using library written for Ruby.
Simple Example
First of all, let's look at tiny Ruby scripts to access a FTP server with ftplib. The native versions of ftplib are bundled with both Ruby and Python. Though using ftplib via Ruby/Python has no practical meaning, it is a good example.
First, a script using ftplib written in Ruby for Ruby.
require 'ftplib'
ftp = FTP.open('ftp.netlab.co.jp')
ftp.login
ftp.chdir('pub/lang/ruby')
puts ftp.dir
ftp.quit
Very simple. Second, a script using ftplib written in Python for Python
via Ruby/Python.
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
Compare the two scripts. There are not so many differences between them. But the latter script uses a library written for Python!
The big difference is the require expressions at the
beginning and the naming of class FTP. And there are a few
different method names. If you are an experienced Ruby user, you may
roughly understand how to use Ruby/Python. As you see, Ruby/Python is a
extension library which enables Ruby users to utilize the modules
written for Python as if they were written for Ruby.
require 'python'
To use Ruby/Python, You need to load Ruby/Python library.
Ruby/Python library itself is a library named 'python'.
When Ruby/Python is loaded, a module named Py is defined
(under Object). The functionalities of Ruby/Python is
provided as modules, classes and module functions under Py
module.
eval and exec
Py.eval(PythonExpression) Py.exec(PythonStatement)
The most simple way to use Python functionalities is to call module
functions eval and exec in Py
module. These module functions are equivalent of Python's
eval builtin function and exec
statement. Py.eval evaluates a Python expression and
returns the value of the expression. Py.exec executes
Python statements.
Example:
list = Py.eval('[1, 2, 3]') # returns a Python list object
Py.exec('print "hello world"') # print "hello world" to stdout
obj.method(...) obj.method?(...) # returns true or false
You can invoke methods of Python objects. If the method name is
suffixed by '?', the value Python method returned is tested as Boolean
value and returns true or false. This
notation is needed because there are no simple mapping between Python
boolean values and Ruby boolean values. If you expect boolean value,
you must append '?'. (Python provides no special objects representing
boolean value. In Python None, 0, empty list,
etc. represent false, and the other objects represent true. Usually
Python API uses 0 to represent false. But 0
is true in Ruby.)
Example:
dict = Py.eval('{"One": 1, "Two": 2}') # Python dictionary (hash) object
dict.keys # ["One", "Two"]
dict["Two"] # 2
dict.has_key("Three") # 0 (true in Ruby)
dict.has_key?("Three") # false
obj.method([...,] Py::AS_KEYWORD, key1 => val1, key2 => val2, ...) obj.method([...,] Py::KW, key1 => val1, key2 => val2, ...)
Python methods can receive keyword arguments. But Ruby currently
doesn't support keyword arguments. So Ruby/Python do it in it's own
fashion as shown above. A special constant Py::AS_KEYWORD
is introduced. (Py::KW is shorthand.) If
Py::AS_KEYWORD is found in the argument list, the remaining
part of the arguments is interpreted as a hash including keyword
arguments. Each keyword name must be a string or a symbol id.
Example:
ftp = Py::Ftplib::FTP.new ftp.connect(Py::AS_KEYWORD, 'host' => 'ftp.netlab.co.jp', 'port' => 21) # string keyword ftp.login(Py::KW, :user => 'anonymous', :passwd => 'mail address') # id keyword
obj.method(...) {|arg| ...}
When a block is given to Python method invocation, a Proc
object is created and passed as the last argument. Proc
objects looks callable objects from Python. So the above
expression is equivalent to the following one:
obj.method(..., Proc.new{|arg| ...})
require 'python/module'
Ruby/Python replaces builtin function require. The new
require interprets library names prefixed with
'python/' specially. When Library names prefixed with
'python/' is passed, the specified Python module is
imported. This is the same as following Python statement.
import module
Imported Python modules look as if they are Ruby modules defined
under Py module. When the name of a Python module begins
with lower case letters, corresponding Ruby module is defined with
capitalized name. Python modules are also refered by attribute
reference to the Py module.
Example: Access to Python sys module.
require 'python' # Py module is defined require 'python/sys' # Py::Sys module is defined Py::Sys # Reference by constant. Py.sys # Attribute reference to Py module. Same as Py::Sys
Functions and attributes defined in Python modules are provided as modules functions.
Example:
require 'python/math' # Py::Math is defined Py::Math.sqrt(2) # 1.41421 Py::Math.pi # 3.14159
Python classes and types are mapped to Ruby classes. These classes
are defined as subclasses of Py::Object. Thus all Python
objects are instance of Py::Object.
Example:
list = Py.eval('[1, 2, 3]') # Python list type object
list.type # Py::Types::ListType class
list.is_a?(Py::Object) # true
Invocation of class method new defined in each Ruby
class correspond to Python class creates a class instance. The
arguments passed to new is used as arguments in Python
instance creation.
Example:
require 'python/ftplib'
ftp = Py::Ftplib::FTP.new('ftp.netlab.co.jp')
# Equivalent to following Python expression.
# ftplib.FTP('ftp.netlab.co.jp')
Objects go across the boundary between Ruby and Python when they are passed as argument to method invocation. Such objects are automatically converted to the destination language's objects. And when objects are returned as a result of method invocation, opposite conversion is performed.
Following objects are converted to the destination language's native objects, that is to say, passed by value.
| Ruby object | Python object | Note |
|---|---|---|
nil | None |
|
true | 1 | (1) |
false | 0 | (1) |
String instance | string | |
Integer instance |
Plain Integer or Long Integer | |
Float instance |
Floating Point Number |
Integer instance. If you want 0/1 to
false/true conversion, suffix
method name with '?'.
Ruby Module or Class is defined for each
Python module, class and type. Thus these objects are converted to each
other.
Objects not mentioned above is passed by reference.
When a Python object is passed by reference to Ruby, the object is
converted to an instance of a Py::Object's subclass. This
object holds the reference to the Python object, and behaves as a
proxy to the Python object. When a message is sent to the
proxy object, the message is forwarded to the Python object.
When a Ruby object is passed by reference to Python, the object looks as if it is a extension type object from Python. This object also holds the reference to the Ruby object, and forward messages.