アクセス制御の抜け穴 (Python/Ruby編)
ある既存クラスの private なメソッドを外から使えるかどうかという話。
他人の触れてはならないプライベートな領域に踏み入りたい! なんていう不埒な欲望が首をもたげて現れた時点で9割9分どっか不健全なわけで、すぐに首を洗って出直すか首を括って死ぬべきですが、それでも往々にしてそんな時のための抜け穴を、僕たち私たちのプログラミング言語は用意してくれていたりするようです。*1
言語としてみれば、そういった裏技が使えることが必ずしも悪なのかどうか。全くないといざというときに困る…かどうかはともかく少なくとも困るかもしれないという漠然とした不安がつきまとうということはあるかもしれない。
それにまぁ、パズル的に使うのは全然アリだと思います。むしろそっちがメイン。そういうの好きな人多いですしね。
というわけで前置きが長くなりましたが私が知ってる各言語についてまとめてみます (その言語をふつうに使っている人にとっては常識といっていい内容だとは思います)。
Pythonの場合
Python はゆるいアクセス制御機構として、アンダースコア2つで始まり、アンダースコア1つ以下で終わるメンバ名 __hoge が _クラス名__hoge に置き変わるという仕組みを採っています。
名前が変わるだけなので、クラス名を知っていれば外からアクセスできます。楽勝です。
class Hoge: def func(self): print "Hoge func" def __priv(self): print "Hoge private" x = Hoge() x.func() x.__priv() # error x._Hoge__priv() # accessible
ほんとに名前が置換されるだけなので、こういったことも可能です。
class Hoge: def _Fuga__priv(self): print "Fuga private" class Fuga(Hoge): def func(self): self.__priv() x = Fuga() x.func()
具体的に何かに使えるのかは知りませんが、何かメタなことを考える上でいろいろとやりやすそうではあります。
Rubyの場合
Ruby のアクセス制御は少し特殊で、private にするとレシーバ付きで呼ぶことができなくなります。x.hoge とか self.hoge でアクセスできないわけですね*2。
なので基本的には、当該クラスまたはそのサブクラスのメソッド定義の中で直接呼ぶしかなくなります。逆にいえば、(protected ではなく) private であってもサブクラスからアクセスすることができるということです。
class Hoge private def priv puts "Hoge private" end end class Fuga < Hoge def priv; super end # public end x = Hoge.new x.priv # error y = Fuga.new y.priv # ok
ところが一方、自分のクラスのメソッドであっても、自分以外のオブジェクトに対してprivateメソッドを呼び出せません。
class Hoge def call_priv(obj) obj.priv end end x = Hoge.new x.call_priv(Hoge.new) # error
これはあんまり嬉しくないですね。こういう場合は protected にしておくのが普通です。
まぁ Ruby はわりといろんな抜け道の多い言語なので、例えばクラス定義を変更して、一時的に public にしてやるなんてことができてしまいます。
x = Hoge.new class Hoge; public :priv end # public に変更 x.priv class Hoge; private :priv end # 証拠隠滅☆
また instance_eval を使えば
x.instance_eval{ priv }
このように対象クラスの内部のコンテキストが自由に得られてしまいます。この方法ならアクセサメソッドが提供されていないフィールドにさえも外側から自由にアクセスできます。