为何weakproxy在Python中不总是保持等价?
MySQLDb使用弱引用来防止游标和连接之间出现循环依赖。
但是,你可能会从关于弱引用的文档中期待,仍然可以测试它们是否相等。然而:
In [36]: interactive.cursor.connection.thread_id()
Out[36]: 4267758
In [37]: interactive.web_logic.conns.primary.thread_id()
Out[37]: 4267758
In [38]: interactive.cursor.connection == interactive.web_logic.conns.primary
Out[38]: False
In [39]: interactive.cursor.connection
Out[39]: <weakproxy at 0x3881c60 to Connection at 0x94c010>
In [40]: interactive.web_logic.conns.primary
Out[40]: <_mysql.connection open to 'xendb01' at 94c010>
我怎么知道这些连接是否是一样的呢?
3 个回答
0
如果这个对象是一个标准的弱引用(weakref),你需要调用它才能得到真正的对象。
import weakref
class Test(object): pass
a = Test()
b = weakref.ref(a)
a is b() # True
a == b() # True
不过在这里使用弱引用似乎不太对:如果我建立了一个连接,从中创建了一个游标(cursor),然后把连接对象丢掉,游标应该还是有效的。除非连接对象保存了所有游标的列表,否则不应该有循环依赖。在这种情况下,应该把那个列表设置为弱引用。
1
把非代理对象用 weakref.proxy 包裹起来,然后使用身份运算符:
>>> interactive.cursor.connection is weakref.proxy(interactive.web_logic.conns.primary)
True
调用 weakref.proxy() 两次会返回同一个代理对象。
3
我一直觉得 weakref.proxy
的设计和实现有点不太靠谱。看看这个…:
>>> import weakref
>>> ob=set(range(23))
>>> rob=weakref.proxy(ob)
>>> rob==ob
False
>>> rob.__eq__(ob)
True
...确实很奇怪!实际上,我使用 weakref
的时候,主要是用弱键或者有时候是弱值的字典;不过 weakref.ref
比它上面的代理包装要靠谱得多:
>>> wr=weakref.ref(ob)
>>> wr()==ob
True
要“调用”这个引用才能得到对象(如果对象已经消失了,就会得到 None),这让它变得不太透明(所以一个数据库 API 模块在遵循 API 的同时是无法做到这一点的)。我不明白 MySqlDb 为什么需要弱游标到连接的引用,但如果他们真的需要,我也能理解他们为什么觉得必须使用代理而不是引用。不过,为了这种透明性,代价可真高啊!
顺便说一下,“显式的 __eq__
”技巧(或者用 __cmp__
,这取决于底层对象的类型)可能会对你有帮助,尽管这确实不太优雅!