2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

初心者がPythonからRubyに移った時に気を付けるべきところ

Last updated at Posted at 2023-10-29

はじめに

プログラミング初学者の筆者がPythonを独学で2か月程学んだ後、Rubyを1ヶ月程勉強した際に無意識的に誤解してしまったところ、気を付けるべきところをまとめてみました。
間違っている箇所があればぜひご指摘ください。

returnは不必要

Rubyでは最後に評価された式がメソッドの返り値になりreturnは必要ない。
returnはメソッドを途中で脱出する場合に使われることが多い。
意図しない値が返っている時はこの仕様を思い出したい。

Ruby
def add(a, b)
  a + b # returnを書く必要がない
end

puts add(1, 2)
# => 3

関数(メソッド)のスコープ

Rubyはローカル変数の参照は定義されたスコープ内のみ。
メソッド定義の外側で宣言されたローカル変数のスコープは、メソッド定義の内側には及ばない。
以下の a はグローバル変数のように使えない。

Python
a = 1

def inner():
    b = 2
    print(a + b)

inner()

# => 3
Ruby
a = 1

def inner
  b = 2
  print(a + b)
end

inner

# =>`inner': undefined local variable or method `a' for main:Object (NameError)

Rubyだとグローバル変数の宣言は頭に$をつける(非推奨とされる)

Ruby
$a = 1

def inner
  b = 2
  print($a + b)
end

inner

# => 3

クラスへのアクセス権

  • Python
    ・デフォルトで隠蔽されていない。
    ・ _, __ を変数名・メソッド名の頭につけると隠蔽される。
  • Ruby
    ・デフォルトで隠蔽されている。
    ・インスタンス変数:アクセスメソッドを設定すれば隠蔽が解除される。(attr_accessor 等)
    ・メソッド:private / protected / public の設定により隠蔽レベルを制御できる。

ex)インスタンス変数nameの参照

Python
class Person:
    def __init__(self, name):
        self.name = name

taro_obj = Person("taro")
print (taro_obj.name)

# => taro
Ruby
class Person
  def initialize(name)
    @name = name
  end
end

taro_obj = Person.new('taro')
puts taro_obj.name

# => undefined method `name' for #<Person:0x000001edc13e8e28 @name="taro"> (NoMethodError)

Rubyでは直接インスタンス変数にアクセスできないのでエラー。参照用のメソッドかアクセサメソッドを設定する。

Ruby
class Person
  # アクセサメソッド
  attr_accessor :name

  def initialize(name)
    @name = name
  end

  # もしくはここで外部からの参照用メソッド
  # def name
    # @name
  # end
end

taro_obj = Person.new('taro')
puts taro_obj.name
# => taro

最初かなり躓いたポイント。

モジュール

  • Python
    ファイル名がモジュール名(test.pyならtestがモジュール名)
  • Ruby
    module 構文で定義されるコード。モジュール名はmoduleの中で定義される。

文字列がミュータブル(mutable)

ミュータブルなオブジェクトとは、作成後にその状態を変更すること(破壊的変更)ができるオブジェクトのこと。
逆にイミュータブルは作成後に変更できない(そもそも破壊的メソッドが定義されていない)オブジェクトのこと。
Pythonの文字列はイミュータブルだがRubyの文字列はミュータブル
代わりにイミュータブルな文字列として振る舞うシンボルがある(内部的には整数)

ex)文字列の先頭を大文字にしてみる

Python
s = "sample"
s[0] = "S"
print(s)

# => TypeError: 'str' object does not support item assignment
Ruby
s = "sample"
s[0] = "S"
print(s)

# => Sample

freezeメソッドを付けることで破壊的な処理を防ぐことが出来る。
マジックコメント # frozen_string_literal: trueをファイルの一番上入れて文字列リテラルのオブジェクトを一気にfreezeも出来る。

Ruby
s = "sample"
s.freeze
s[0] = "S"
print(s)
# =>  can't modify frozen String: "sample" (FrozenError) 
Ruby
# frozen_string_literal: true

s = "sample"
# s.freeze
s[0] = "S"
print(s)
# => can't modify frozen String: "sample" (FrozenError)

Rubyでは最後に!が付く多くは破壊的メソッド (!が付くから必ずしも破壊的メソッドとは限らないし逆も然り。単に危険的な意味合いが強い。)
ex) 文字列を大文字にするupcaseは非破壊的メソッド、upcase!は破壊的メソッド

真偽値

  • Python
    偽: None と False、数値型におけるゼロ: 0, 0.0, 0j, Decimal(0), Fraction(0, 1)、空のシーケンスまたはコレクション: '', (), [], {}, set(), range(0)
  • Ruby
    偽: nilとfalseのみ。

特に数値型0と空文字列、空配列などの扱いには注意する。
Rubyでif条件文中に == のところを = と誤って書いてしまったときに意図しないtrueでif文の中が実行されてしまう。

not and or でなく ! && ||

not, and, or は演算子としての優先度が低いので同じように使うと意図しないエラーが起きる。
優先度【高】→【低】
! → && → || → not → (and or)
and と or の優先順位は同じなので左から評価される。

Ruby
t1 = true
t2 = true
f1 = false

!f1 || t1
# => true
not f1 || t1
# => false

t1 || t2 && f1
# => true
t1 or t2 and f1
# => false

and orは条件分岐に用いず制御フローに用いる。

除算の挙動

整数同士の除算が異なる

  • Python3
    //もしくは/演算子を用いる。
    //:整数同士の割り算は整数。小数以下切り捨て。
    /:整数同士の割り算で割り切れても小数が返る(float型に型が変換される)

  • Ruby
    /演算子を用いる。
    整数同士の割り算は整数。小数以下切り捨て

  • (Python2)
    /演算子を用いる。整数同士の割り算は整数。小数以下切り捨て。

/演算子を同じように使わない。

Python3
a = 12 // 4
print(a)        # => 3
print(type(a))  # => <class 'int'>

b = 12 / 4
print(b)        # => 3.0
print(type(b))  # => <class 'float'>
Ruby
c = 12 / 4
puts c          # => 3
puts c.class    # => Integer

比較の連鎖

Rubyでは数学のように繋げて比較演算子を書くことが出来ない。

Ruby
a = 2
if 0 < a < 3
  puts a
end
# => error

a = 2
if 0 < a && a < 3
  puts a
end
# => 2

配列、リストの差分

言語の違いではないかもしれない。Pythonでのリストに近いものとしてRubyでは配列が使われるが、 - 演算子を使うとその要素の差分が取得できる。

Python
list_a = [1, 2, 3, 4]
list_b = [3, 4]
diff_list = list_a - list_b
print(diff_list)
# => TypeError: unsupported operand type(s) for -: 'list' and 'list'
Ruby
arr_a = [1, 2, 3, 4]
arr_b = [3, 4]
diff_arr = arr_a - arr_b
p diff_arr
# => [1, 2]

For文

Rubyであまりfor文は多用しない。その代わりeachメソッドやmapメソッドといった繰り返しメソッドが主に使われる。

その他

case文が存在する、    
range()関数、pass文が存在しない

終わりに

PythonとRubyは似ている言語なので学習しやすい反面、無意識的にPython知識でRubyを補完してしまっていた。
まだまだ駆け出しの身なので注意深く学んでいきたい。
この記事も少しずつ増やしていきたい。

参考文献

Ruby公式リファレンス
Python公式ドキュメント
プロを目指す人のためのRuby入門(チェリー本)
PythonからRubyへ(Ruby公式)
PythonとRubyの変数のスコープのまとめ

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?