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灯, 水草

请输入看板名称,例如:BuyTogether站内搜寻

TOP