分かりにくいタイトルですみません。
言葉で説明するより、実際にコードを見た方がどういうことか分かりやすいと思います。
環境ですが、RubyはRuby 2.0、PythonはPython 3.2となっています。
##さて、書くか
試しにRubyでこんなコードを書いてみます。
#スコープの関係でグローバル変数
$msg = "Hello, Before World!"
def say(str = $msg)
puts str
end
say #=> Hello, Before World!
$msg = "Hello, After World!"
say #=> Hello, After World!
出力はコメントの通りのはずです。
Pythonで同じようなコードを書くとこうなります。
msg = "Hello, Before World!"
def say(str = msg):
print(str)
say() #=> Hello, Before World!
msg = "Hello, After World!"
say() #=> Hello, Before World!
同じようなコードですが、 say
関数の出力結果が変わっています。
これで、タイトルの意図は大体掴んで頂けたでしょうか?
##解説です
まず、RubyもPythonも任意の式をデフォルト引数の値に書くことが出来ます。で、RubyとPythonではこの値が評価されるタイミングが違います。
Rubyでは、 関数が呼び出されたとき に、
Pythonでは、 関数が定義されたとき にデフォルト引数の値が評価されます。
※Rubyはメソッドだろおい、とか細かいことは言わない。
$msg = "Hello, Before World!"
def say(str = $msg)
puts str
end
say() #=> Hello, Before World!
$msg = "Hello, After World!"
say() #=> このタイミングで $msg が評価される
#=> よって Hello, After World!
msg = "Hello, Before World!"
def say(str = msg): #この時点で msg が評価されて Hello, Before World! になっている
print(str)
say() #=> Hello, Before World!
msg = "Hello, After World!"
say() #=> すでに str の値は決定しているから、 Hello, Before World!
なので、さっきみたいな結果になるわけですね。
##雑感…
個人的には、Rubyの方が色んなことが出来るので面白いなぁ、と思うんですが、どちらの挙動も 自然 なものなので悪くはないです。(PythonはVMの仕様的にああせざるを得ないのかなぁ、とも)
ちなみに、Rubyではデフォルト引数以外の引数を参照することができるので、さらにデフォルト引数内で自身を呼び出して再帰させることで、
#見ろよこいつ、デフォルト引数で再帰してやがる…
def fact(n,ret = n <= 1 ? 1 : fact(n-1) * n)
ret
end
puts fact(10) #=> 3628800
みたいなことが出来たりします。
Rubyってやつは…(絶句)。
ついでに、Pythonではこの特性を利用して、ローカルスコープ代わりにしたりするみたいです(多分)。
say_n = []
for i in range(0,10):
#say_n.append(lambda: print(i))
#だと i の値が更新されていってしまうためダメ
say_n.append(lambda j = i: print(j))
#のようにして、 i を束縛する必要がある
say_n[2]() #=> 2
say_n[4]() #=> 4
非常に雑多なTipsでしたが、こういう細かい言語ごとの仕様の違いにハマるとなかなか抜け出せなくなるので、知っていて損するものでもない気がします。覚えておいて、少しでも役立つことがあったら幸いです。
RubyもPythonも門外漢なので、少なからず何か間違っていることがあるかもしれません。それを見つけた際は、コメント欄かTwitter( @alucky0707 )でそっと鳩尾にツッコミをかましていただけるとありがたいです。
##参考ページ
- rubyのデフォルト引数を使った再帰処理 - Rubyのデフォルト引数で再帰できるってここで知った。
- Pythonのスコープ云々って話を聞いてこの記事を考えたんだけど、どこにあったか忘れた…。