[Python] Name Mangling 的大範圍攻擊
早安,真是好久不見了,這學期快忙翻,而這一篇文只是確認生存用的,所以並不會太長。
- Name Mangling
在 Python 中撰寫 class 時,只要在屬性名字的前面加上兩個底線(如 __spam),這個屬性的名字最後就會被重組成 _Cls__spam 這樣的形式,這項技術稱作 Name Mangling,提供了類似其他語言的私用屬性。
但要注意的是,就如前面所提到的,類別外部的實例依舊可用重組後的名稱來存取該屬性,所以這並不能做為防止有心人士修改重要資料的方法;事實上,Name Mangling 的存在是為了避免外界對特定屬性的意外存取,或不小心覆寫了父類別的屬性、方法。
但其實,Name Mangling 有個鮮為人知的陰謀...
根據 Python 官方文件表示:
舉例來說:
有時候我們會使用 __dict__ 或其他函式來存取實例的屬性,而如果目標屬性會被 mangling 所影響,那麼我們對 __dict__ 所使用的鍵或傳入其他函式的引數就必須是已經重組過的名字:
這次講的內容其實並不容易造成什麼問題,只是偶然在 werkzeug 的 source code 裡發現一些神奇的地方,再經過自己實驗後觀察到的現象。
最後,感謝看到這裡的你,如果發現這篇文章有什麼瑕疵或想回饋的,都可以在下面留言。
參考資料:
1) Python language reference 6.2.1 - Identifiers
2) Python language reference 9.6 - Private Variables
後記:
上一篇文明明說學期中會跟大家見面的,但還是拖到了過年XD
而上次說的新系列目前因為我幼小的心靈受到了一些摧殘所以近期可能還不會推出(笑)
但要注意的是,就如前面所提到的,類別外部的實例依舊可用重組後的名稱來存取該屬性,所以這並不能做為防止有心人士修改重要資料的方法;事實上,Name Mangling 的存在是為了避免外界對特定屬性的意外存取,或不小心覆寫了父類別的屬性、方法。
class MyClass:
def __init__(self):
self.__bar = 1
m = MyClass()
print(m._MyClass__bar) # 1
但其實,Name Mangling 有個鮮為人知的
- 哪些東西會被 Mangling?
根據 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。
- 那些不會被重組的
有時候我們會使用 __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
而上次說的新系列目前因為
留言
張貼留言