luggage baggage

Machine learning, data analysis, web technologies and things around me.

Python 使いのための、Python と Ruby の違い(基本文法)

こんにちは。吉田弁二郎です。

今回は、web 開発の主力言語の一つとなっている Ruby の文法について、Python との違いをまとめます。文法の完全な紹介を目指すのではなく、特に紛らわしい項目の差分に注目していきます。そのため、Ruby 独自の文法的な事項(module/include/extend, Proc など)には触れていませんのでご容赦を。なお、今回は Python 3.5.2 および Ruby 2.3.1 で確認しています。

制御構文

範囲の記述(1から5まで)
# Python
range(1, 6)
# Ruby:以下はいずれも同じ
1..5
1...6
1.upto(5) 
偽と判定されるもの
# Python
None
False
[]  # 空の配列
''  # 空の文字列
0
# Ruby
nil
false
ループ中、次の繰り返しに移動する方法
# Python:continue キーワードを使う
sum_even = 0
for i in range(10):
    if i % 2 == 0:
        continue  # <-
    else:
        sum_even += 1
# Ruby:next キーワードを使う
sum_even = 0
(0...10).each do |i|
  if i % 2 == 0
    next  # <-
  else
    sum_even += 1
  end
end
ハッシュに対する for 文
# Python:キーを取り出す
h = {'hoge': 1, 'foo': 2}
res = []
for item in h:
    res.append(item)
#=> res = ['hoge', 'foo']
# Ruby:キーと値の組を取り出す
h = { hoge: 1, foo: 2 }
for item in h
  res.push item
end
#=> res = [[:hoge, 1], [:foo, 2]]

Python で Ruby のこの for 文と同様のことをする場合は

for item in h.items():
    res.append(item)

となります。Ruby の方は見づらいので

h.each do |key, value|
  ...
end

とした方がよい気がします。

ワンライナー条件文
# Python:
a = 1
'one' if a == 1 else 'not one'  #=> 'one'
# Ruby:
a = 1
a == 1 ? 'one' : 'not one'  #=> 'one'
例外処理
# Python:
from sys import exc_info
a = 1
try:
    a.hoge  # エラー送出
except Exception as e:
    info = exc_info()
    print("error class: {}".format(info[0]))
    print("error message: {}".format(info[1]))
finally:
    print('done.')
# Ruby:
a = 1
begin
  a.hoge  # エラー送出
rescue => e
  puts e.class
  puts e.message
ensure
  puts 'done'
end

配列

インデックス参照
# Python:範囲外のインデックス参照はエラーとなる
a = [0, 1]
a[2]  #=> IndexError
# Ruby:範囲外のインデックス参照は nil を返す
a = [0, 1]
a[2]  #=> nil

これはハッシュでも同様です。Ruby は寛容ですね。

文字列

文字列への変数の挿入
# Python:format 関数を使う
lang_name = 'python'
"hello {name}!".format(name=lang_name)  #=> "hello python!"
# Ruby:
lang_name = 'ruby'
"hello #{lang_name}!"  #=> "hello ruby!"
文字列表示のための関数
# Python
print('hoge!')
# Ruby
puts 'hoge!'  # 文末に改行がつく。Python の print と同様
print 'hoge!'  # 文末に改行がつかない

関数

可変長引数
# Python
def func(*args):
    print(args)

a = [1, 2, 3]
func(a)  #=> ([1, 2, 3], )
func(*a)  #=> (1, 2, 3)
# Ruby
def func(*args)
  print args
end

a = [1, 2, 3]
func(a)  #=> [[1, 2, 3]]
func(*a)  #=> [1, 2, 3]

Python ではタプルが、Ruby では配列が返る以外は同じ挙動ですね。func(*a) の * は配列を引数展開して渡すことを表します。

可変長キーワード引数
# Python
def func(a=0, b=0):
    print("a is {}, b is {}".format(a, b))

func()  #=> a is 0, b is 0
func({"a": 1, "b": 2})  #=> a is {"a": 1, "b": 2}, b is 0
func(**{"a": 1, "b": 2})  #=> a is 1, b is 2
# Ruby:引数の波括弧を省略可能
def func(a: 0, b: 0):
  puts "a is #{a}, b is #{b}"
end

func()  #=> a is 0, b is 0
func("a": 1, "b": 2)  #=> a is 1, b is 2
func(**{"a": 1, "b": 2})  #=> a is 1, b is 2

クラス

宣言(継承しない場合)
# Python
class Klass(object):
    ...
# Ruby
class Klass
  ...
end
初期化メソッドとインスタンス化
# Python
class Klass(object):
    def __init__(self, *args, **kwargs):
        ....

klass = Klass(*args, **kwargs)
# Ruby
class Klass
  def initialize(*args, **kwargs)
    ...
  end
  ...
end

klass = Klass.new(*args, **kwargs)
クラス内でのインスタンス変数の表現
# Python
self.var
# Ruby
@var
クラスメソッドの定義
# Python
class Klass(object):
    ...
    @classmethod  # デコレータ
    def hoge(cls):  # cls を引数に取る
        ...
# Ruby
class Klass
  ...
  def self.hoge
    ...
  end
end
変数・メソッドの隠蔽
Python Ruby
・通常は隠蔽されていない
・ _, __ を変数名・メソッド名の頭につけると隠蔽される
・通常は隠蔽されている
・インスタンス変数:アクセスメソッドを設定すれば隠蔽が解除される(attr_accessor 等)
・メソッド:private / protected / public の設定により隠蔽レベルを制御できる

その他

コメント
# Python:これが1行コメント
"""
 これは複数行コメント。空白・改行が反映される。
"""
# Ruby:これが1行コメント

=begin
これが複数行コメント。= の前に空白があると機能しない。
ヒアドキュメントについては割愛。
=end
外部ファイル(ライブラリ)の読み込み
# Python
import hoge
from hoge import foo  # hoge から foo だけを読み込み
# Ruby
require 'hoge'  # hoge で定義されたローカル変数以外を全て読み込み
「モジュール」という名称
Python Ruby
・コードをまとめた .py ファイルのこと
・「モジュールを import する」等
・module で定義されるコード片のこと
・「モジュールを include する」等


参考:

たのしいRuby 第5版

たのしいRuby 第5版

初めてのPython 第3版

初めてのPython 第3版