DT日記

家を離れた自宅警備員の日記

Ruby vs Python! 〜def vs def〜

本記事はRuby Advent Calendar jp: 2011の参加記事です。いえーい。本日は五日め……?

スタートの12/1はこのAdvent Calendarの主催者にして中学生Rubyコミッターことそらはー(@)のCod.note - Coffeescripting with Ruby、12/2はudzura (Uchio Kondo)さんのblog.udzura.jp - almost-sinatra.rb の深淵を覗くでした。

3日めと4日めは……?

12/3のRubySpec の走らせ方と、12/4のRuby の力じゃ!いまこそ Capybara を使え!!!も公開されてました。

本記事中の「Rubyなメソッドについて」 訂正があります。no6vさんありがたうございました。あとでたぶん補足記事書きます。

対象読者

Ruby初心者・初級者・中級者向け。具体的にはRubyPythonにあんまり詳しくないひと。

これを読んで言語デザインの差からプログラミング言語に興味を持ってくれると楽しいかな☆

既にRubyPythonをある程度にご存じの方は既知のことが多い気がします。

凡例

基本的にはRubyは1.9.3のirbで、Pythonは2.7.2でipythonを使って実行してます(pythonの対話環境とは出力が若干異ります)。掲載するコードの先頭にプロンプト(>>> など)は付けず、強調したい式の返り値(函数・メソッド呼び出し)に#=> を付けて表示します。

コードの見ためを比較してみよう

# ruby
def foo()
  return "foooo"
end
foo() 
#=> "foooo"
# python
def foo():
    return "foooo"

foo()
#=> "foooo"

うん、そっくり。

ここはRubyPythonで似たやうに書けるポイント…なのだけれど、RubyPythonではコードの意味がまったく異る。つまり、Rubyではメソッドを、Pythonでは函数を宣言してゐる。Rubyのメソッドを函数のやうに使ふことはできるが、メソッドとは函数の別名ではないし、Pythonのメソッドは、これとは別にある。

また、メソッドと函数との差とは別に、fooとだけ書いたときにRubyPythonの挙動の違ひが顕れる。

# ruby
foo
#=> "foooo"
# python
foo
#=> <function __main__.foo>

これはRubyのメソッド呼び出しとPython函数呼び出しの記法の差異である。

Rubyのメソッドはfooと書いてもfoo()と書いてもメソッドを呼び出せる。

一方で、Pythonでは<function __main__.foo>と表示されてしまってゐる。この表示はPythonデバッグ用出力で、この函数を呼び出すにはfoo()と書くしかない。

なんだかそれでもRubyではdefを多用してるひとが多い気がするな……?

メソッドと函数は同じ…?

さて、とりあへず函数とメソッドは同じやうなものだと假定して、こんな問題を考へてみる。str_to_funcといふ函数/メソッドを作ってみよう。この函数/メソッドは文字列型の_strと、コールバック函数(相当の)_funcといふ二つの引数をとる。そして、_func_strで適用した値を返す。

C言語で書くとこんな感じ…?

#include <stdio.h>
#include <stdlib.h>

int str_to_func(const char *_str, int (*_cb) (const char *)){
        return _cb(_str);
}

int myint(const char *_str){
        return atoi(_str);
}

int main(void){
        int n = 0;
        n = str_to_func("1234", myint);
        printf("%d\n", n);

        return 0;
}

さて、これをPythonで書くとこんな感じになる。

# python
def str_to_func(_str, _cb):
    return _cb(_str)

def myint(obj):
    return int(obj)

str_to_func("1234", myint)
#=> 1234

うむ、これは自然に書ける。さて、これをRubyで書くとどうなるか。

# ruby
def str_to_func(str,cb)
  cb.call(str)
end

def myint(obj)
  obj.to_i
end

str_to_int("1234",method(:myint))
#=> 1234

Cよりは短いけどPythonよりは素直じゃない気がするぞ……?

hmm...

と、ここで最初のRubyコードにちょっと付け足しをしてみたい。

class A; end
a = A.new

a.foo()
#=> "foo"

この動きが信じられないと思ったら、コマンドラインirbを起動して逐一動きを確認してみると良い。どんなクラスを作っても、このやうに呼ぶことができるはずである。

これを考へるために、いくつかRuby固有の作法について順を追って考へたい。

Rubyなメソッド

この章の内容には不正確な点、あるいは限られた状況下のみで成立する内容になってゐます。ご注意の上でお読みください。

初めに言った通り、Rubyにおけるdefキーワードは、メソッドを定義する。メソッドであるからには、それは何らかのオブジェクトに属してゐるはずである。

# ruby
def foo
  "#{self}: foooo"
end
def print_self
  p foo
end

class A; end

a = A.new

print_self()
A.print_self()
a.print_self()

このコードはirbでの実行時のみ、この記事の記述通りに動作します。コードをファイルに保存して実行すると、14行めでNoMethodErrorが発生して、動作が停止します。あるいはendclass A; endの間の行にpublic :foo, :print_self追記することで動作させることもできます。

このコードはいままで紹介してきたコードと違ってdef hoge()の形式ではないが、Rubyでは假引数がない場合は()を省くことができる。上までのPythonと見ためを近しくするために付けてゐたが、以下は付けないこととする。ただ、假引数をとらない場合において()のあるなしで意味が変化することはない。

selfは変数であるが、これはメソッドのオブジェクト自身を指す。pはメソッドで、値(オブジェクト)のデバッグ用出力を行ふ。このスクリプトを実行した結果、次のやうな出力を期待する。

"main: foooo"
"A: foooo"
"#<A:0x00000102017888>: foooo"

この記事の目的は構文の差からRubyPythonの差を見出すことなので(それが根幹に位置する問題でありながら)、オブジェクト指向についての説明は省くことにする*1

このコードから確認できることを簡潔にまとめると、トップレベル(classmoduleで階層構造になってゐない箇所)でメソッドを定義すると、便宜上mainオブジェクトのメソッドとして定義される。次に、トップレベルのメソッドは他のクラスのクラスの特異メソッドとしても、インスタンスメソッドとしても定義されてゐる。どうしてこんな現象が起こるのかは……長くなるのでRubyソースコード完全解説参照。具体的には第4章 クラスとモジュールをどうぞ。

トップレベルのメソッドが継承される、までは合ってゐるのですが、rubyで実行する際にはトップレベルで定義されたメソッドはObjectクラスのprivateメソッドとなります。

ではPythonでは…?

Rubydef函数ではなくメソッドを作ると説明した。メソッドとはオブジェクト指向あってこその概念である。ところでPythonRubyと同様にオブジェクト指向機能を持ってゐる。では、Pythonにはメソッドはあるのか?

――ある!

# python
class A(object):
    def foo(self):
        return "foooo"

a = A()
a.foo()
#=> "foooo"

a.foo
#=> <bound method A.foo of <__main__.A object at 0x10176c050>>

Pythonにおけるメソッドはクラスの中で定義された函数のことで、必ずレシーバを第一引数にとる。レシーバとは、この場合はインスタンスのことである。

# python
class X(object):
    def myself(self):
        return self
    
    def bar(self, _str):
        return "bar: {str}".format(str=_str)

x = X()
x.bar("hogehoge")

x2 = x.myself()
x == x2
#=> True

Rubydefがメソッドしか定義できないのに対して、Python函数もメソッドも同様に定義することができる。Pythonのメソッドは定義するときには、selfのぶん、引数を一つ多く書く必要がある。

Rubyに慣れてゐるとselfがよけいなぶんでPythonがめんどくさく見えるかもしれないが、僕がどちらが決定的に優れてゐるとは思はない。一長一短がある。

そしてもうひとつRubyPython

# python
def x():
    def y():
        return "yyyyy"
    return y

x()
#=> ???

先の「メソッドと函数は同じ…?」の動きから考へれば、このx()の出力がどうなるのか想像がつくだろうか。できれば対話環境(python, ipython)で実際に動かしてみてほしい。

続いてRuby

# ruby
def x
  def y
    return "yyyyy"
  end
  return method(:y)
end

x()
#=> ???

さてさて、この結果はどうなるか。これもどうなるか想像してから対話環境(irb)で動かしてみてほしい。

補足しておくと、Rubyのメソッドはオブジェクトではない。しかし、Methodクラスといふものを作ることはできる。

# ruby
def hoge:
  "hogehoge"
end

h1 = hoge
#=> "hogehoge"
# メソッドではなく、メソッド呼び出しの結果(返り値)が代入される

h2 = method(:hoge)
#=> #<Method: Object#hoge>
# Method オブジェクトが代入される

h2.call
#=> "hogehoge"
# Method オブジェクトのcallメソッドを呼び出すことで、元のメソッドが呼び出される

……と、こんなことをつらつら書いてるうちに12/5が終ってしまふので、こんなところでおひらきにしたいと思ひます。

これを機に

  1. Rubyのメソッドが何者なのか
  2. オブジェクト指向とのおいしい付き合い方
  3. Rubyがどうして良いのか、何が悪いのか

みたいなところを考へてもらって、いろいろ調べて知っていただけたら幸せに思ひます。

この文章を読んでそんな意欲が残ったひとは、最後に紹介したmethodメソッドとMethodクラスが本当は一体何者なのかを考へてもらふと、きっとおもしろいんじゃないかと。

関連記事

この二回の記事で「Rubyでの函数」について詳しく触れてゐるので、興味があればこちらもどうぞ。

さて、次のRuby Advent Calendarは…?

Ruby Advent Calendar jp: 2011の6日めは、Asakusa.rbの@さんです。おそらくこちらの/var/log/messagesに掲載されるのではないかと。それではよろしくお願ひします。

*1:ほんとはくどい説明を書きかけてたのだけれど、あまりに本筋から外れるのでオミットしました