アクセス制御の抜け穴 (Python/Ruby編)

ある既存クラスの private なメソッドを外から使えるかどうかという話。

他人の触れてはならないプライベートな領域に踏み入りたい! なんていう不埒な欲望が首をもたげて現れた時点で9割9分どっか不健全なわけで、すぐに首を洗って出直すか首を括って死ぬべきですが、それでも往々にしてそんな時のための抜け穴を、僕たち私たちのプログラミング言語は用意してくれていたりするようです。*1

言語としてみれば、そういった裏技が使えることが必ずしも悪なのかどうか。全くないといざというときに困る…かどうかはともかく少なくとも困るかもしれないという漠然とした不安がつきまとうということはあるかもしれない。
それにまぁ、パズル的に使うのは全然アリだと思います。むしろそっちがメイン。そういうの好きな人多いですしね。

というわけで前置きが長くなりましたが私が知ってる各言語についてまとめてみます (その言語をふつうに使っている人にとっては常識といっていい内容だとは思います)。

まずはPythonRubyから。

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 }

このように対象クラスの内部のコンテキストが自由に得られてしまいます。この方法ならアクセサメソッドが提供されていないフィールドにさえも外側から自由にアクセスできます。

*1:ところで「首を洗って出直す」って正しい日本語でしょうか? 「首を洗って待つ」なら分かりますが…。

*2:例外として名前が = で終わるメソッドは self 経由でアクセスできます