レシピ追加、ブロック…そんなものは無いけれども
Python Recipe は『Rubyレシピブック 第2版 268の技』が元ネタなので Python にはない、もしくは Python に不要なものもある。「012:関数の呼び出し制限」、「237:汚染モード(taintモード)を使う」、「155:ワンライナでファイルを更新する」、「015:特異メソッド」などなど。でも、これらのレシピにも Python 的代替案は示したいなぁ、と長らく考えていた。
そして、ある意味無茶なレシピの代替案、第一弾は『010:ブロック』となった。 Ruby の目玉のひとつ、ブロック構文に挑戦。これはまだなんとかなるはず、と思い至り。
結果は…微妙か。
Python にもクロージャの概念はあるので同等のことはできるが、読みやすさではかなわず。ブロック変数を明記したものと比べるならば(まて、普通 yield を使うだろ)、そしてブロックの中身が Python の式一つで書き表せるものならば(制限きついって)、似ないこともない(こら、 each_with_index ブロック付きメソッドを勝手に for 文に直すな)。
# Ruby ブロック引数明記 # 定義 def map_with_index(list, &b) result = [] list.each_with_index{|item, idx| value = b.call(item, idx) result << value } return result end # 呼び出し p map_with_index([1, 2, 3]){|item, idx| item * idx} # => [0, 2, 6]
# Python # 定義 def map_with_index(list_, b): result = [] for idx, item in enumerate(list_): value = b(item, idx) result.append(value) return result # 呼び出し print map_with_index([1, 2, 3], lambda item, idx: item * idx) # => [0, 2, 6]
ブロックの内容が式一つで収まらない場合は、事前に def 文で関数定義することになる。
# Python # 呼び出し def block(item, idx): return item * idx print map_with_index([1, 2, 3], block) # => [0, 2, 6]
追記、 each_with_index 込みで真似するとこうなる? 本当は組み込み型 list に each_with_index メソッドを加えられればよいのだが、基本的な組み込み型(list, int など)には属性、メソッドの追加はできないので関数にして模倣。
# Python def each_with_index(self, b): for idx, item in enumerate(self): value = b(item, idx) def map_with_index(list_, b): result = [] def block(item, idx): value = b(item, idx) result.append(value) each_with_index(list_, block) return result print map_with_index([1, 2, 3], lambda item, idx: item * idx) # => [0, 2, 6]