[Python] Name Mangling 的大範圍攻擊

早安,真是好久不見了,這學期快忙翻,而這一篇文只是確認生存用的,所以並不會太長。
  • Name Mangling
 在 Python 中撰寫 class 時,只要在屬性名字的前面加上兩個底線(如 __spam),這個屬性的名字最後就會被重組成 _Cls__spam 這樣的形式,這項技術稱作 Name Mangling,提供了類似其他語言的私用屬性。
 但要注意的是,就如前面所提到的,類別外部的實例依舊可用重組後的名稱來存取該屬性,所以這並不能做為防止有心人士修改重要資料的方法;事實上,Name Mangling 的存在是為了避免外界對特定屬性的意外存取,或不小心覆寫了父類別的屬性、方法。
class MyClass:
    def __init__(self):
        self.__bar = 1

m = MyClass()
print(m._MyClass__bar)  # 1

 但其實,Name Mangling 有個鮮為人知的陰謀...
  • 哪些東西會被 Mangling?
 「這話是什麼意思?不就是兩個底線開頭的屬性嗎?」看到標題後,有些人應該會這麼問。的確,像上例的 __bar 會是重組的目標,但其實,還有一些東西也是。

 根據 Python 官方文件表示:
When an identifier that textually occurs in a class definition begins with two or more underscore characters and does not end in two or more underscores, it is considered a private name of that class.  ...  This transformation is independent of the syntactical context in which the identifier is used.
 前面的粗體字說明了被重組的名稱必須以 2 個或以上的底線所開頭,且結尾最多只有一個底線;而後面的粗體則是說,這個 mangling 轉換並不侷限於特定的語境(syntactical context),亦即不只有 self.__spam 這裡的 __spam 會被重組,任何出現在 class 內文的變數,只要名稱形式符合,都會被重組。

 舉例來說:
class MyClass:
    def hello(self):
        __foo = 101  # this will be mangling
        return _MyClass__foo
    
    def __print(self):
        print("this will be mangling, too")

    __cls_attr = 'another identifier that will be mangling'

m = MyClass()
print(m.hello())
m._MyClass__print()
print(MyClass._MyClass__cls_attr)
 上例中列出了會被名稱重組的三種可能情況,其中比較有趣的是 hello 方法中的 __foo,在原始方法內文中,我們可以同時使用兩種名稱來存取他,分別是 __foo 和 mangling 後的 _MyClass__foo。
  • 那些不會被重組的
 前面的引文也有提到,被重組的名稱必須是個 identifier,因此單純的字串內出現 __foo 這樣的文字並不會被替換,不過這會有什麼問題嗎?
 有時候我們會使用 __dict__ 或其他函式來存取實例的屬性,而如果目標屬性會被 mangling 所影響,那麼我們對 __dict__ 所使用的鍵或傳入其他函式的引數就必須是已經重組過的名字:
class MyClass:
   def __init__(self):
       self.__data = 101
   
   def get_data(self):
       return self.__dict__['_MyClass__data'] 
 
m = MyClass()
m.get_data()  # 101
 雖然這個例子不太好,但能夠展示我想表達的就行了。

 這次講的內容其實並不容易造成什麼問題,只是偶然在 werkzeug 的 source code 裡發現一些神奇的地方,再經過自己實驗後觀察到的現象。

 最後,感謝看到這裡的你,如果發現這篇文章有什麼瑕疵或想回饋的,都可以在下面留言。

參考資料:
  1) Python language reference 6.2.1 - Identifiers
  2) Python language reference 9.6 - Private Variables


後記:
 上一篇文明明說學期中會跟大家見面的,但還是拖到了過年XD
 而上次說的新系列目前因為我幼小的心靈受到了一些摧殘所以近期可能還不會推出(笑)

留言

這個網誌中的熱門文章

[C] 每天來點字串用法 (2) - strcpy()、strncpy()

[Python] *args 和 **kwargs 是什麼?一次搞懂它們!

[C] 每天來點字串用法 (5) - strcat()、strncat()