为何weakproxy在Python中不总是保持等价?

1 投票
3 回答
915 浏览
提问于 2025-04-15 13:52

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__,这取决于底层对象的类型)可能会对你有帮助,尽管这确实不太优雅!

撰写回答