Rubyのメソッド周りの動作まとめ
今までRubyにあまり触れていなかったため、 初めてのRuby、 メタプログラミングRuby 第2版、 Rubyのしくみ -Ruby Under a Microscope- を一気に読んだ。
この記事の内容は、学習の記録&復習としてメソッド周りの動作をまとめたもの。
公式ドキュメントは読んだがそれ以上はやってないという方にはもしかしたら役に立つかもしれない。 もうすでにバリバリ使ってる方には当たり前の内容かもしれない。
コードの動作は
$ ruby -v ruby 2.3.1p112 (2016-04-26) [x86_64-linux-gnu]
で確認した。
目次
- メソッドの実行
- メソッドの探索
- メソッドの定義
メソッドの実行
メソッドの呼び出し時の動作:
- 常にレシーバが設定される
- レシーバを明示しない場合は呼び出したスコープでの
self
がレシーバとなる - メソッド内の
self
は呼び出されたときのレシーバとなる
def whoami(); self; end whoami # => main //トップレベルのselfはmainオブジェクト(Objectクラスのインスタンス) class C whoami # => C def f whoami end end o = C.new o # => #<C:0x000000008c7c88> o.f # => #<C:0x000000008c7c88> // レシーバはo. whoami内のselfはo
send
を使えばメソッド名のシンボルを使って呼び出すこともできる。
その際のレシーバはsend
のレシーバが使われる。
# つづき o.send(:f) # => #<C:0x000000008c7c88> // レシーバはo. whoami内のselfはo
メソッドの探索
オブジェクトは内部で
- そのクラスへの参照をもつ
- スーパークラスへの参照をもつ(クラスオブジェクトの場合)
これらの参照先はclass
メソッドやsuperclass
メソッドで得られるものとは異なる。
説明のため以下ではそれらの参照を単にそれぞれklass, superという言葉で表す。
(klass、superはRubyのC言語での実装で使われているポインタの名前。
もしかしたら新しいバージョンでは実装や名前が変わっているかもしれないが、大きく実装が変わらない限り当分このイメージで問題ないだろう)
obj.method_name()としてメソッドを実行すると、
- objのklassが指すクラスにmethod_nameが定義されているか
- そのsuperのクラスに定義されているか
- そのsuperのクラスに定義されているか
- …
と探索し、最初に見つかったものを実行する。
ancestors
でそのsuperのチェーンを確認できる。
class P; end module M; end module N include M # N -super-> M end N.ancestors # => [N, M] class C < P # C -super-> P include N # C -super-> N -super-> M -super-> P end C.ancestors # => [C, N, M, P, Object, Kernel, BasicObject] N.ancestors # => [N, M] // インクルード時にはmoduleのコピーをインクルードするのでここで M -super-> Pになっているわけではない
メソッドを探索し最後まで見つからなければ、最初に戻ってmethod_missing
メソッドを探索する。
BasicObject
クラスにはもともとmethod_missing
が定義されているため何もしなければそれが実行される。
BasicObject.private_instance_methods(false) # => [..., :method_missing, ...]
method_missing
を定義すれば存在しないメソッドを処理することもできる。(ゴーストメソッドとよばれる)
class C def method_missing(name) "#{name} is called!" end end C.new.hogehoge # => "hogehoge is called!" C.new.helloworld # => "helloworld is called!"
メソッドの定義
def
によってメソッドを定義する方法は2つ
def
+ メソッド名で定義(def methodname() ...
)def
+ オブジェクト名.メソッド名 で定義(def obj.methodname() ...
)
1. def methodname() ...
で定義
この場合、def
が現れた場所のカレントクラスのインスタンスメソッドとして定義される。
("カレントクラス"はメタプログラミングRuby 第2版で使われていた用語で、公式の用語ではなさそう)
カレントクラスはスコープが参照しているクラスのことで、次のようになっている。
# カレントクラス: Object module M # カレントクラス: M class C # カレントクラス: C. hogeはCのインスタンスメソッドとなる def hoge() # カレントクラス: self.class end end # カレントクラス: M end p M::C.instance_methods(false) # => [:hoge]
2. def obj.methodname() ...
で定義
この場合、そのobjの固有のクラス(特異クラス)のインスタンスメソッドに定義される。 (以下、objオブジェクトの特異クラスには#を付けて#objと表す)
class C def func end end obj = C.new obj2 = C.new def obj.sfunc # objの特異クラス#objのインスタンスメソッドとして定義 end obj.sfunc obj2.sfunc # Error p obj.class.instance_methods(false) #=> [:func] p obj.singleton_class.instance_methods(false) #=> [:sfunc]
上記のobj.sfunc
はobj
の特異メソッド(singleton method)と呼ばれる。
このときメソッドの探索で説明したklassとsuperは
obj -klass-> #obj -super-> C -super-> ...
のようになっており、#objにsfunc、Cにfuncが定義されているため、前述のメソッドの探索でobj.func
もobj.sfunc
も実行できる。
obj2.sfunc
がErrorなのはその探索するクラスにsfunc
が定義されていないため。
クラスでは…
クラスもオブジェクトであるため、
def C.classfunc end
とすればクラスの特異メソッド(クラスメソッド)が定義でき、C.classfunc
で実行できる。
class ClassName ... end
内のselfはそのクラスになることから、上記は以下のようにも定義できる。
class C def self.classfunc # ここでのselfはC. Cの特異クラスに定義 end end
class << object
とするとそのオブジェクトの特異クラスをカレントクラスとしてオープンできるので、下のようにもかける。
class << C # このスコープでのカレントクラスはCの特異クラス def classfunc end end
クラスの特異クラスのことをメタクラスとよぶこともある。
スーパークラスの特異クラスは特異クラスのスーパークラスなので、クラスメソッドはサブクラスでも実行できる。
class P def self.classf "ok" end classf # => "ok" end class C < P classf # => "ok" // selfはC. C -klass-> #C -super-> #P end C.singleton_class.superclass == P.singleton_class # => true
P --klass-> #P (def classf) ^ ^ super super ^ ^ C --klass-> #C
クラスがオブジェクトであるように、特異クラスもオブジェクトであるため特異クラスの特異クラスも作成できる。(使い道があるかどうかは別として)
トップレベル
トップレベルでのカレントクラスはObjectであり、 トップレベルでオブジェクト指定なしでメソッドを定義するとObjectのprivateメソッドとして定義される。 この事実と、
- レシーバを指定せずにメソッドを実行すると
self
がレシーバとして設定される Object
はあらゆるクラスのスーパークラス
ということから、トップレベルで定義した関数はあらゆる場所でレシーバなし実行できる。
(Object
のサブクラスではないクラスも作れるため、全ての場所で実行できるわけではない。)
def top; "ok!" end Object.private_instance_methods(false) # => [..., :top, ...] class C top() # => ok! // selfはC。 CはClassクラスのインスタンス def hoge top() end end C.new.hoge # => ok! // hoge内のselfはCクラスのインスタンス class Clean < BasicObject top() # => ok! // selfはClean, CleanはClassクラスのインスタンス def fuga top() end end Clean.ancestors # => [Clean, BasicObject] Clean.new.fuga # NoMethodError // topがundefined. selfのメソッド探索ルートにObjectがいないため
ちなみにprivateメソッドはレシーバを明示できないため(privateの制約)、 トップレベルのメソッドはレシーバなし実行できるというよりは、 レシーバを明示したobj.method()の形式では実行できない。
特異クラスはいつ作られるのか(おまけ)
p ObjectSpace.count_objects[:T_CLASS] # => 612 class C; end p ObjectSpace.count_objects[:T_CLASS] # => 614 // Cと#Cが作られた? o = C.new p ObjectSpace.count_objects[:T_CLASS] # => 614 def o.f; end p ObjectSpace.count_objects[:T_CLASS] # => 615 // #oがつくられた?
クラスの数だけで判断すると、クラスの特異クラスはクラス定義時に作成される、 クラスでないオブジェクトの特異クラスは必要になったら作成される、っぽい。 (前半の、クラス定義時に2つクラスができる、というのはRubyのしくみ -Ruby Under a Microscope-に記載されている内容。)
- 作者: Yugui
- 出版社/メーカー: オライリージャパン
- 発売日: 2008/06/26
- メディア: 大型本
- 購入: 27人 クリック: 644回
- この商品を含むブログ (251件) を見る
- 作者: Paolo Perrotta,角征典
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/10/10
- メディア: 大型本
- この商品を含むブログ (3件) を見る
Rubyのしくみ -Ruby Under a Microscope-
- 作者: Pat Shaughnessy,島田浩二,角谷信太郎
- 出版社/メーカー: オーム社
- 発売日: 2014/11/29
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (4件) を見る