Python 板


LINE

※ 引述《rexrainbow ( hua)》之銘言: : 操作物件內的__dict__字典相當於操作物件內的屬性存取. 雖然仍有些不同. : obj.__dict__[name] = value # obj.name = value : 然而以 obj.__dict__["__setattr__"] = new_fn 來取代現有的 __setattr__ 時 : 遇到一個問題(使用python2.6): : class aKlass: : var = 0 : def a_fn(self): : print "aKlass.a_fn" : def __setattr__(self, name, value): : print "aKlass.__setattr__",name,value : def new_a_fn(self): : print "new_a_fn" : def new_setattr_fn(self,name,value): : print "new_setattr_fn",name,value : aKlass內含兩個函數, a_fn, __setattr__. : 另準備兩個對應的函數new_a_fn, new_setattr_fn : 希望使用aKlass.__dict__[ ]的方式取代 -- : aObj = aKlass() : aObj.a_fn() : aKlass.__dict__["a_fn"] = new_a_fn : aObj.a_fn() : aObj.var = 1 : aKlass.__dict__["__setattr__"] = new_setattr_fn : aObj.var = 1 : 執行結果: : aKlass.a_fn : new_a_fn : aKlass.__setattr__ var 1 : aKlass.__setattr__ var 1 : aKlass.__dict__["a_fn"] = new_a_fn 的確用new_a_fn取代了原先的a_fn. : 但似乎 aKlass.__dict__["__setattr__"] = new_setattr_fn 沒有達到預期的效果. : 不知原因為何? : (雖然使用 setattr(aKlass,"__setattr__",new_setattr_fn) 就可以取代原函數了.) 你測試的兩個 case 並不算是全等的,如果讓兩者在做法更接近,應該是: aKlass.__dict__["a_fn"] = new_a_fn aObj.a_fn() aKlass.__dict__["__setattr__"] = new_setattr_fn aObj.__setattr__('var', 1) 接下來回到為什麼修改 aKlass.__dict__ 後,在如此的 statement: aObj.var = 1 上看不到作用? 1. 這只會發生在 old-style class。 (new-style class 的 __dict__ 則是 immutable,實際上是個 proxy) 2. aKlass object(不是指 aObj)除了 __dict__ 裡有 reference 指涉原本在 constructing aKlass 過程中所建立的 __setattr__ function(這個 function 是指 aKlass.__setattr__.im_func),aKlass object 本身也有一個欄位存著 該 function 的位址。(可以看看 Python 安裝後一併附的 classobject.h) 我把 CPython 2.6.5 的 classobject.h 部分內容節錄在此: typedef struct { PyObject_HEAD PyObject *cl_bases; /* A tuple of class objects */ PyObject *cl_dict; /* A dictionary */ PyObject *cl_name; /* A string */ /* The following three are functions or NULL */ PyObject *cl_getattr; PyObject *cl_setattr; PyObject *cl_delattr; } PyClassObject; aKlass 在記憶體裡的結構會分別有 __getattr__/__setattr__/__delattr__ 的位址(if any)。 當執行過此 statement: aKlass.__dict__['__setattr__'] = new_setattr 只變更了 cl_dict 所指涉的 dict 的狀態,並沒有變更 cl_setattr 指向另一個 function,而 aObj.var = 1 statement 會直接使用 cl_setattr 的值(一種優化, 省了 dictionary lookup 動作),所以 aObj.var = 1 還是調用了原來的 __setattr__ function。 如果你使用下列的 statement(任一)來變更 __setattr__ 屬性: aKlass.__setattr__ = new_setattr_fn setattr(aKlass, '__setattr__', new_setattr_fn) 則會變更 cl_setattr 欄位指向 new_setattr_fn,所以 aObj.var = 1 有預期中的表現。 接下來我要透過一些手法來變更 aKlass 在記憶體中其 cl_setattr 欄位的值, 看看有什麼效果。 * 有心一試的人請在 console mode 執行 python REPL(interpreter),不要 使用 IDLE 或是 PythonWin Editor 等 IDE 環境。 沿用 aKlass/new_a_fn/new_setattr_fn 的定義。 接下來 import ctypes 套件(已內建於 Python 2.5+) from ctypes import * a = aKlass() wrapper = py_object(aKlass) p = cast(addressof(wrapper), POINTER(POINTER(c_uint32))).contents print id(aKlass.__setattr__.im_func) # address of original __setattr__ # function for x in xrange(10): print x, p[x] 執行完以上的程式碼後,大概可以看到類似如下的 output: 12325616 0 3 1 505362952 2 11210800 3 11416608 4 12313696 5 0 6 12325616 7 0 8 7 9 505362336 這表示 p[6] 是 cl_setattr 欄位(p[5] 是 cl_getattr,p[7] 是 cl_delattr, 兩者皆是 0,因為 aKlass 沒有定義 __getattr__, __delattr__)。 old_setattr_addr = p[6] # 記下原 __setattr__ 的位址 p[6] = id(new_setattr_fn) a.var = 1 # see output p[6] = old_setattr_addr a.var = 2 # see output 以上幾個 statement 的輸出應該如下: new_setattr_fn var 1 aKlass.__setattr__ var 2 --



※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 220.136.175.158 ※ 編輯: sbrhsieh 來自: 220.136.175.158 (12/12 00:44)
1F:推 jacobcrab:甚善哉, 領教了. 12/12 06:59
2F:推 rexrainbow:推~ 12/12 07:15
3F:推 cobrasgo:哇賽,玩這麼深,長知識了 12/15 00:26
4F:→ cobrasgo:另外請教一下old-style class和new-style class的分野是? 12/15 00:28
5F:→ cobrasgo:2.x和3的差別嗎?謝謝 12/15 00:28
6F:→ sbrhsieh:2.2 開始有 new-style class。以 new-style class 為 12/15 09:19
7F:→ sbrhsieh:base 者為 new-style class。object 是 new-style class. 12/15 09:19







like.gif 您可能會有興趣的文章
icon.png[問題/行為] 貓晚上進房間會不會有憋尿問題
icon.pngRe: [閒聊] 選了錯誤的女孩成為魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一張
icon.png[心得] EMS高領長版毛衣.墨小樓MC1002
icon.png[分享] 丹龍隔熱紙GE55+33+22
icon.png[問題] 清洗洗衣機
icon.png[尋物] 窗台下的空間
icon.png[閒聊] 双極の女神1 木魔爵
icon.png[售車] 新竹 1997 march 1297cc 白色 四門
icon.png[討論] 能從照片感受到攝影者心情嗎
icon.png[狂賀] 賀賀賀賀 賀!島村卯月!總選舉NO.1
icon.png[難過] 羨慕白皮膚的女生
icon.png閱讀文章
icon.png[黑特]
icon.png[問題] SBK S1安裝於安全帽位置
icon.png[分享] 舊woo100絕版開箱!!
icon.pngRe: [無言] 關於小包衛生紙
icon.png[開箱] E5-2683V3 RX480Strix 快睿C1 簡單測試
icon.png[心得] 蒼の海賊龍 地獄 執行者16PT
icon.png[售車] 1999年Virage iO 1.8EXi
icon.png[心得] 挑戰33 LV10 獅子座pt solo
icon.png[閒聊] 手把手教你不被桶之新手主購教學
icon.png[分享] Civic Type R 量產版官方照無預警流出
icon.png[售車] Golf 4 2.0 銀色 自排
icon.png[出售] Graco提籃汽座(有底座)2000元誠可議
icon.png[問題] 請問補牙材質掉了還能再補嗎?(台中半年內
icon.png[問題] 44th 單曲 生寫竟然都給重複的啊啊!
icon.png[心得] 華南紅卡/icash 核卡
icon.png[問題] 拔牙矯正這樣正常嗎
icon.png[贈送] 老莫高業 初業 102年版
icon.png[情報] 三大行動支付 本季掀戰火
icon.png[寶寶] 博客來Amos水蠟筆5/1特價五折
icon.pngRe: [心得] 新鮮人一些面試分享
icon.png[心得] 蒼の海賊龍 地獄 麒麟25PT
icon.pngRe: [閒聊] (君の名は。雷慎入) 君名二創漫畫翻譯
icon.pngRe: [閒聊] OGN中場影片:失蹤人口局 (英文字幕)
icon.png[問題] 台灣大哥大4G訊號差
icon.png[出售] [全國]全新千尋侘草LED燈, 水草

請輸入看板名稱,例如:BabyMother站內搜尋

TOP