2014年9月23日火曜日

Rubyistが歩んでみる蛇の道はPython その5

Dive into Python 3のchapter 6をざっと読んでみた。

Pythonの関数はオブジェクトである。これはRubyの関数(メソッド)と非常に大きく異なる点だ。Pythonだと

>>> def foo():
..  print('foo')
..
>>> f = foo
>>> f()
foo
のように変数に直接関数を代入したり、代入した変数経由で元の関数を呼び出したりできる。…と言うか
>>> foo = 1
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
>>> foo = f
>>> foo()
foo
こんなことができてしまうということは、fooは定数ですらないようだ。Pythonのdef文は、JavaScriptのfunctionと同じように関数オブジェクトを作って、それを何の変哲も無い普通の変数に代入しているだけみたい。

とにもかくにも、関数がオブジェクトであるということは、100やら'abc'やらと同じように普通に受け渡しが出来る。ここは正直Rubyの弱いところで、基本的に最大1つのブロックをメソッドに渡すことしかできない。一応:funcのようなシンボルでメソッド名を渡したり、lambda等でProcオブジェクトを作って渡したり、呼び出し先でevalしてもらうコードを文字列で渡したりすることはできるのだけれど、自然に関数を扱えるとは言い難い。まあ、Rubyではメソッドが特別扱いされているが故に

print 'ruby'
のように()を付けなくてもメソッド呼び出し出来るのは、ちゃちゃっと使い捨てコードを書くときに楽は楽なんだけど。

もう1点。RubyのProcにはないけどPythonの関数オブジェクトにはある機能に、中断/再開がある。

>>> def gen():
...   v = 0
...   while v <= 50:
...     yield v
...     v += 10
...
>>> g = gen()
>>> type(g)
<class 'generator'>
このように、内部でyield文を使った関数はgeneratorクラスのオブジェクトを返す。そして
>>> next(g)
0
>>> next(g)
10
>>> next(g)
20
>>> next(g)
30
>>> next(g)
40
>>> next(g)
50
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
このようにnext文でgeneratorを呼び出す度に、関数オブジェクトがyieldした値が呼び出し元に返され、また関数オブジェクトの方はyieldしたところで中断する。ノンプリエンプティブなスレッドと言えなくもない。このgeneratorの嬉しいのは、リスト等と同じように
>>> for i in gen():
...   print(i)
...
0
10
20
30
40
50
こんな感じでイテレータとして使えるところ。イテレータの話は次の章で深追いするのかな?

0 件のコメント:

コメントを投稿