Pythonのマングリングとは?
Pythonではクラス内に __
で始まり、 __
で終わらない名前をマングリングします。
マングリングとは例えば
class T: def foo(self): self.__bar = "bar" print(self.__bar) print(self._T__bar) T().foo()
としたときに、barが2回出力されます。Pythonではクラス内で定義された __bar
を特別扱いして self._T__bar
とすることでprivateメンバ変数に見せかける仕組みのようです。もちろんクラス内からは self.__bar
でアクセス出来ます。
ルールは簡単。__
がついて __
で終わらない変数を _クラス名__変数名
にするだけです。
マングリングの闇
import編
さて、上の例だとselfの変数にマングリングが行われましたが、実はマングリングはクラス内で定義されたもの 全て に対して行われます。
例
$ ls | grep test __test2 _T__test1
というディレクトリがあった場合、
class T: import __test1 import __test2
を実行すると
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in T ImportError: No module named '_T__test2'
こうなるわけです。
import __test1
はマングリングされて _T__test1
名前空間パッケージを見に行くため正常にimportされます。
import __test2
はマングリングされて _T__test2
を見に行きますが、__test2
は見ないため ImportError
となります。
これバグではとちょっと思ったり…
import __test1
を逆アセンブルします。
3 8 LOAD_CONST 1 (0) 10 LOAD_CONST 2 (None) 12 IMPORT_NAME 3 (_T__test1) 14 STORE_NAME 3 (_T__test1) 16 LOAD_CONST 2 (None) 18 RETURN_VALUE
つまり、 import __test1
はコンパイル時に import _T__test1
にされるのです。
exec編
class Foo: def __init__(self): exec("self.__foo = 2") def bar(self): print(self.__foo) Foo().bar()
を実行すると
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 5, in bar AttributeError: 'Foo' object has no attribute '_Foo__foo'
となるわけです。つまり exec
はマングリングされないんですね。マングリングはスクリプトのコンパイル時に行われるので exec
だとマングリングされません。
クラススコープについて
import
ですでにやりましたが、クラススコープの識別子も変換しないと、メソッドから参照できなくなるのでとにかく変換します。
class A: import sys as __sys print(__sys) print(_A__sys) def f(self): print(self.__sys) print(self._A__sys) A().f()
は
<module 'sys' (built-in)> <module 'sys' (built-in)> <module 'sys' (built-in)> <module 'sys' (built-in)>
となります。
何でもかんでも変換する
マングリングに例外はありません。識別子は全部変換されます。
class A: def x(self): __y = 100 print(dir()) A().x()
は
['_A__y', 'self']
となります。
class A: def f(self): import sys sys.__omg = 100 A().f() import sys sys._A__omg 100
もはや意味がわかりませんね。
global編
当然globalもです。
class A: global __x __x = 100 _A__x
100
oh…
引数編
class A: def f(self): def a(__arg): print(dir()) a(1) A().f()
は
['_A__arg']
(‘A`)
最後に
マングリングの仕様について、如何だったでしょうか?こんな知識嫌がらせにしか使えないですが、もし遭遇した場合には役に立つことを願います。
私はcafebabepyの実装にこれを盛り込まないといけないので憂鬱です。。